create-dubhe 1.2.0-pre.98 → 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.
Files changed (361) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +16 -16
  4. package/templates/101/sui-template/docker-compose.testnet.yml +73 -0
  5. package/templates/101/sui-template/packages/client/src/app/page.tsx +545 -41
  6. package/templates/101/sui-template/packages/client/src/app/providers.tsx +12 -4
  7. package/templates/101/sui-template/packages/contracts/Pub.localnet.toml +4 -4
  8. package/templates/101/sui-template/packages/contracts/counter.lock.json +20 -0
  9. package/templates/101/sui-template/packages/contracts/deployment.ts +8 -3
  10. package/templates/101/sui-template/packages/contracts/dubhe.config.json +59 -13
  11. package/templates/101/sui-template/packages/contracts/dubhe.config.ts +1 -4
  12. package/templates/101/sui-template/packages/contracts/metadata.json +1015 -68
  13. package/templates/101/sui-template/packages/contracts/package.json +7 -4
  14. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/dapp_key.move +45 -31
  15. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/error.move +8 -0
  16. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/genesis.move +30 -13
  17. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/init_test.move +19 -14
  18. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/resources/counter2.move +198 -0
  19. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/resources/counter2withkey.move +90 -0
  20. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/resources/value.move +30 -20
  21. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/user_storage_init.move +26 -0
  22. package/templates/101/sui-template/packages/contracts/src/counter/sources/scripts/deploy_hook.move +6 -11
  23. package/templates/101/sui-template/packages/contracts/src/counter/sources/systems/counter.move +23 -11
  24. package/templates/101/sui-template/packages/contracts/src/counter/sources/tests/counter.move +15 -14
  25. package/templates/101/sui-template/packages/contracts/src/dubhe/Move.lock +33 -30
  26. package/templates/101/sui-template/packages/contracts/src/dubhe/Move.toml +8 -3
  27. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/dapp_key.move +45 -31
  28. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/error.move +170 -0
  29. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/genesis.move +16 -13
  30. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/init_test.move +8 -14
  31. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/account_key.move +1 -3
  32. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/dapp_service.move +1776 -137
  33. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/data_key.move +1 -20
  34. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/events.move +864 -25
  35. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/scripts/deploy_hook.move +15 -16
  36. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/scripts/migrate.move +0 -3
  37. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/address_system.move +25 -17
  38. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/dapp_system.move +2802 -343
  39. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/utils.move +1 -1
  40. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/address.move +173 -55
  41. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/dapp_test.move +769 -0
  42. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/fee_test.move +2965 -0
  43. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/integration_test.move +303 -0
  44. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/listing_test.move +1422 -0
  45. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/scene_session_test.move +269 -0
  46. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/session_test.move +624 -0
  47. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/storage_test.move +700 -0
  48. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/typed_object_test.move +177 -0
  49. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/typed_scene_test.move +452 -0
  50. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/user_storage_test.move +160 -0
  51. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/utils/math.move +4 -3
  52. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/utils/type_info.move +2 -2
  53. package/templates/contract/sui-template/dubhe.config.ts +0 -1
  54. package/templates/contract/sui-template/package.json +3 -3
  55. package/templates/contract/sui-template/src/dubhe/Move.lock +33 -30
  56. package/templates/contract/sui-template/src/dubhe/Move.toml +8 -3
  57. package/templates/contract/sui-template/src/dubhe/sources/codegen/dapp_key.move +45 -31
  58. package/templates/contract/sui-template/src/dubhe/sources/codegen/error.move +170 -0
  59. package/templates/contract/sui-template/src/dubhe/sources/codegen/genesis.move +16 -13
  60. package/templates/contract/sui-template/src/dubhe/sources/codegen/init_test.move +8 -14
  61. package/templates/contract/sui-template/src/dubhe/sources/core/account_key.move +1 -3
  62. package/templates/contract/sui-template/src/dubhe/sources/core/dapp_service.move +1776 -137
  63. package/templates/contract/sui-template/src/dubhe/sources/core/data_key.move +1 -20
  64. package/templates/contract/sui-template/src/dubhe/sources/core/events.move +864 -25
  65. package/templates/contract/sui-template/src/dubhe/sources/scripts/deploy_hook.move +15 -16
  66. package/templates/contract/sui-template/src/dubhe/sources/scripts/migrate.move +0 -3
  67. package/templates/contract/sui-template/src/dubhe/sources/systems/address_system.move +25 -17
  68. package/templates/contract/sui-template/src/dubhe/sources/systems/dapp_system.move +2802 -343
  69. package/templates/contract/sui-template/src/dubhe/sources/systems/utils.move +1 -1
  70. package/templates/contract/sui-template/src/dubhe/sources/tests/address.move +173 -55
  71. package/templates/contract/sui-template/src/dubhe/sources/tests/dapp_test.move +769 -0
  72. package/templates/contract/sui-template/src/dubhe/sources/tests/fee_test.move +2965 -0
  73. package/templates/contract/sui-template/src/dubhe/sources/tests/integration_test.move +303 -0
  74. package/templates/contract/sui-template/src/dubhe/sources/tests/listing_test.move +1422 -0
  75. package/templates/contract/sui-template/src/dubhe/sources/tests/scene_session_test.move +269 -0
  76. package/templates/contract/sui-template/src/dubhe/sources/tests/session_test.move +624 -0
  77. package/templates/contract/sui-template/src/dubhe/sources/tests/storage_test.move +700 -0
  78. package/templates/contract/sui-template/src/dubhe/sources/tests/typed_object_test.move +177 -0
  79. package/templates/contract/sui-template/src/dubhe/sources/tests/typed_scene_test.move +452 -0
  80. package/templates/contract/sui-template/src/dubhe/sources/tests/user_storage_test.move +160 -0
  81. package/templates/contract/sui-template/src/dubhe/sources/utils/math.move +4 -3
  82. package/templates/contract/sui-template/src/dubhe/sources/utils/type_info.move +2 -2
  83. package/templates/contract/sui-template/src/template/Move.toml +12 -0
  84. package/templates/contract/sui-template/src/template/sources/codegen/dapp_key.move +47 -0
  85. package/templates/contract/sui-template/src/template/sources/codegen/genesis.move +34 -0
  86. package/templates/contract/sui-template/src/template/sources/codegen/init_test.move +23 -0
  87. package/templates/contract/sui-template/src/template/sources/codegen/user_storage_init.move +27 -0
  88. package/templates/contract/sui-template/src/template/sources/scripts/deploy_hook.move +13 -0
  89. package/templates/contract/sui-template/src/template/sources/scripts/migrate.move +7 -0
  90. package/templates/nextjs/sui-card-duel/.gitignore_ +51 -0
  91. package/templates/nextjs/sui-card-duel/README.md +213 -0
  92. package/templates/nextjs/sui-card-duel/docker-compose.yml +34 -0
  93. package/templates/nextjs/sui-card-duel/mprocs.yaml +17 -0
  94. package/templates/nextjs/sui-card-duel/package.json +43 -0
  95. package/templates/nextjs/sui-card-duel/packages/client/.gitignore_ +48 -0
  96. package/templates/nextjs/sui-card-duel/packages/client/.prettierignore +1 -0
  97. package/templates/nextjs/sui-card-duel/packages/client/.prettierrc.json +8 -0
  98. package/templates/nextjs/sui-card-duel/packages/client/eslint.config.mjs +22 -0
  99. package/templates/nextjs/sui-card-duel/packages/client/next.config.mjs +8 -0
  100. package/templates/nextjs/sui-card-duel/packages/client/package.json +47 -0
  101. package/templates/nextjs/sui-card-duel/packages/client/postcss.config.mjs +8 -0
  102. package/templates/nextjs/sui-card-duel/packages/client/src/app/admin/ArenaPanel.tsx +307 -0
  103. package/templates/nextjs/sui-card-duel/packages/client/src/app/admin/page.tsx +622 -0
  104. package/templates/nextjs/sui-card-duel/packages/client/src/app/brawl/[id]/page.tsx +559 -0
  105. package/templates/nextjs/sui-card-duel/packages/client/src/app/collection/page.tsx +202 -0
  106. package/templates/nextjs/sui-card-duel/packages/client/src/app/components/CardView.tsx +66 -0
  107. package/templates/nextjs/sui-card-duel/packages/client/src/app/components/GameShell.tsx +294 -0
  108. package/templates/nextjs/sui-card-duel/packages/client/src/app/components/icons.tsx +101 -0
  109. package/templates/nextjs/sui-card-duel/packages/client/src/app/duel/[id]/page.tsx +477 -0
  110. package/templates/nextjs/sui-card-duel/packages/client/src/app/favicon.ico +0 -0
  111. package/templates/nextjs/sui-card-duel/packages/client/src/app/globals.css +85 -0
  112. package/templates/nextjs/sui-card-duel/packages/client/src/app/hooks/useGame.ts +313 -0
  113. package/templates/nextjs/sui-card-duel/packages/client/src/app/layout.tsx +24 -0
  114. package/templates/nextjs/sui-card-duel/packages/client/src/app/leaderboard/page.tsx +123 -0
  115. package/templates/nextjs/sui-card-duel/packages/client/src/app/lib/game.ts +77 -0
  116. package/templates/nextjs/sui-card-duel/packages/client/src/app/lib/scenes.ts +226 -0
  117. package/templates/nextjs/sui-card-duel/packages/client/src/app/market/page.tsx +418 -0
  118. package/templates/nextjs/sui-card-duel/packages/client/src/app/page.tsx +427 -0
  119. package/templates/nextjs/sui-card-duel/packages/client/src/app/providers.tsx +69 -0
  120. package/templates/nextjs/sui-card-duel/packages/client/tailwind.config.ts +21 -0
  121. package/templates/nextjs/sui-card-duel/packages/client/tsconfig.json +30 -0
  122. package/templates/nextjs/sui-card-duel/packages/contracts/.gitignore_ +51 -0
  123. package/templates/nextjs/sui-card-duel/packages/contracts/.prettierignore +1 -0
  124. package/templates/nextjs/sui-card-duel/packages/contracts/.prettierrc.json +8 -0
  125. package/templates/nextjs/sui-card-duel/packages/contracts/Dockerfile +5 -0
  126. package/templates/nextjs/sui-card-duel/packages/contracts/Pub.localnet.toml +13 -0
  127. package/templates/nextjs/sui-card-duel/packages/contracts/card_duel.lock.json +66 -0
  128. package/templates/nextjs/sui-card-duel/packages/contracts/docker-compose.yml +34 -0
  129. package/templates/nextjs/sui-card-duel/packages/contracts/dubhe.config.ts +150 -0
  130. package/templates/nextjs/sui-card-duel/packages/contracts/logs/combined.log +31383 -0
  131. package/templates/nextjs/sui-card-duel/packages/contracts/package.json +49 -0
  132. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/config.ts +17 -0
  133. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-brawl.ts +270 -0
  134. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-duel.ts +272 -0
  135. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-lobby.ts +177 -0
  136. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-market.ts +219 -0
  137. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-session.ts +342 -0
  138. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/query-dapp-revenue.ts +95 -0
  139. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/seed.ts +92 -0
  140. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/withdraw-dapp-revenue.ts +56 -0
  141. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/Move.toml +15 -0
  142. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/dapp_key.move +46 -0
  143. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/enums/card_kind.move +47 -0
  144. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/enums/match_state.move +37 -0
  145. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/enums/rarity.move +37 -0
  146. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/error.move +126 -0
  147. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/genesis.move +33 -0
  148. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/init_test.move +23 -0
  149. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/objects/arena.move +137 -0
  150. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/permits/brawl_permit.move +125 -0
  151. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/permits/duel_permit.move +125 -0
  152. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/battle_state.move +255 -0
  153. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/card.move +282 -0
  154. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/deck.move +64 -0
  155. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/game_config.move +267 -0
  156. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/gold.move +205 -0
  157. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/profile.move +255 -0
  158. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/scenes/brawl.move +313 -0
  159. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/scenes/duel.move +313 -0
  160. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/user_storage_init.move +26 -0
  161. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/scripts/deploy_hook.move +32 -0
  162. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/scripts/migrate.move +7 -0
  163. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/arena_system.move +57 -0
  164. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/brawl_system.move +559 -0
  165. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/card_system.move +146 -0
  166. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/duel_system.move +515 -0
  167. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/market_system.move +115 -0
  168. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/player_system.move +37 -0
  169. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/tests/brawl_tests.move +385 -0
  170. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/tests/duel_tests.move +656 -0
  171. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/tests/test_helpers.move +31 -0
  172. package/templates/nextjs/sui-card-duel/packages/contracts/sui-indexer-schema.graphql +5973 -0
  173. package/templates/nextjs/sui-card-duel/pnpm-workspace.yaml +2 -0
  174. package/templates/nextjs/sui-farm/.gitignore_ +51 -0
  175. package/templates/nextjs/sui-farm/README.md +15 -0
  176. package/templates/nextjs/sui-farm/UPGRADE_NOTES.md +97 -0
  177. package/templates/nextjs/sui-farm/docker-compose.yml +34 -0
  178. package/templates/nextjs/sui-farm/mprocs.yaml +17 -0
  179. package/templates/nextjs/sui-farm/package.json +43 -0
  180. package/templates/nextjs/sui-farm/packages/client/.gitignore_ +48 -0
  181. package/templates/nextjs/sui-farm/packages/client/.prettierignore +1 -0
  182. package/templates/nextjs/sui-farm/packages/client/.prettierrc.json +8 -0
  183. package/templates/nextjs/sui-farm/packages/client/eslint.config.mjs +22 -0
  184. package/templates/nextjs/sui-farm/packages/client/next.config.mjs +8 -0
  185. package/templates/nextjs/sui-farm/packages/client/package.json +47 -0
  186. package/templates/nextjs/sui-farm/packages/client/postcss.config.mjs +8 -0
  187. package/templates/nextjs/sui-farm/packages/client/src/app/admin/page.tsx +618 -0
  188. package/templates/nextjs/sui-farm/packages/client/src/app/components/FarmLand.tsx +526 -0
  189. package/templates/nextjs/sui-farm/packages/client/src/app/components/FarmPlotCard.tsx +215 -0
  190. package/templates/nextjs/sui-farm/packages/client/src/app/components/PetAvatar.tsx +868 -0
  191. package/templates/nextjs/sui-farm/packages/client/src/app/components/PetPanel.tsx +1360 -0
  192. package/templates/nextjs/sui-farm/packages/client/src/app/components/PlantModal.tsx +147 -0
  193. package/templates/nextjs/sui-farm/packages/client/src/app/components/ProxyCard.tsx +684 -0
  194. package/templates/nextjs/sui-farm/packages/client/src/app/components/RanchLand.tsx +822 -0
  195. package/templates/nextjs/sui-farm/packages/client/src/app/components/ResourceHUD.tsx +125 -0
  196. package/templates/nextjs/sui-farm/packages/client/src/app/components/ShopPanel.tsx +232 -0
  197. package/templates/nextjs/sui-farm/packages/client/src/app/components/icons/GameIcons.tsx +574 -0
  198. package/templates/nextjs/sui-farm/packages/client/src/app/favicon.ico +0 -0
  199. package/templates/nextjs/sui-farm/packages/client/src/app/globals.css +423 -0
  200. package/templates/nextjs/sui-farm/packages/client/src/app/hooks/useWorldPermitId.ts +66 -0
  201. package/templates/nextjs/sui-farm/packages/client/src/app/layout.tsx +23 -0
  202. package/templates/nextjs/sui-farm/packages/client/src/app/leaderboard/page.tsx +185 -0
  203. package/templates/nextjs/sui-farm/packages/client/src/app/lib/crops.ts +97 -0
  204. package/templates/nextjs/sui-farm/packages/client/src/app/market/page.tsx +763 -0
  205. package/templates/nextjs/sui-farm/packages/client/src/app/page.tsx +1173 -0
  206. package/templates/nextjs/sui-farm/packages/client/src/app/providers.tsx +69 -0
  207. package/templates/nextjs/sui-farm/packages/client/src/app/state/index.tsx +5 -0
  208. package/templates/nextjs/sui-farm/packages/client/tailwind.config.ts +25 -0
  209. package/templates/nextjs/sui-farm/packages/client/tsconfig.json +30 -0
  210. package/templates/nextjs/sui-farm/packages/contracts/.gitignore_ +51 -0
  211. package/templates/nextjs/sui-farm/packages/contracts/.prettierignore +1 -0
  212. package/templates/nextjs/sui-farm/packages/contracts/.prettierrc.json +8 -0
  213. package/templates/nextjs/sui-farm/packages/contracts/Dockerfile +5 -0
  214. package/templates/nextjs/sui-farm/packages/contracts/Pub.localnet.toml +13 -0
  215. package/templates/nextjs/sui-farm/packages/contracts/counter.lock.json +14 -0
  216. package/templates/nextjs/sui-farm/packages/contracts/docker-compose.yml +34 -0
  217. package/templates/nextjs/sui-farm/packages/contracts/dubhe.config.ts +178 -0
  218. package/templates/nextjs/sui-farm/packages/contracts/harvest.lock.json +120 -0
  219. package/templates/nextjs/sui-farm/packages/contracts/package.json +48 -0
  220. package/templates/nextjs/sui-farm/packages/contracts/scripts/config.ts +17 -0
  221. package/templates/nextjs/sui-farm/packages/contracts/scripts/query-dapp-revenue.ts +95 -0
  222. package/templates/nextjs/sui-farm/packages/contracts/scripts/read-permit.ts +72 -0
  223. package/templates/nextjs/sui-farm/packages/contracts/scripts/seed.ts +85 -0
  224. package/templates/nextjs/sui-farm/packages/contracts/scripts/withdraw-dapp-revenue.ts +56 -0
  225. package/templates/nextjs/sui-farm/packages/contracts/src/counter/Move.toml +14 -0
  226. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/dapp_key.move +31 -0
  227. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/error.move +10 -0
  228. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/genesis.move +33 -0
  229. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/init_test.move +23 -0
  230. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/resources/counter1.move +64 -0
  231. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/resources/counter2.move +70 -0
  232. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/user_storage_init.move +26 -0
  233. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/scripts/deploy_hook.move +8 -0
  234. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/scripts/migrate.move +8 -0
  235. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/systems/counter.move +13 -0
  236. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/tests/counter.move +28 -0
  237. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/Move.toml +14 -0
  238. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/dapp_key.move +46 -0
  239. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/enums/crop_type.move +47 -0
  240. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/enums/egg_type.move +42 -0
  241. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/enums/pet_rarity.move +37 -0
  242. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/enums/pet_species.move +52 -0
  243. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/error.move +110 -0
  244. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/genesis.move +33 -0
  245. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/init_test.move +23 -0
  246. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/permits/world.move +125 -0
  247. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/carrot.move +136 -0
  248. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/carrot_seed.move +80 -0
  249. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/common_egg.move +136 -0
  250. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/corn.move +136 -0
  251. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/corn_seed.move +80 -0
  252. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/crow_charges.move +162 -0
  253. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/crow_damage.move +207 -0
  254. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/farm_plot.move +261 -0
  255. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/gold.move +80 -0
  256. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pet.move +467 -0
  257. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pet_config.move +372 -0
  258. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pet_hatch.move +162 -0
  259. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pet_slot_index.move +83 -0
  260. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pet_slots.move +64 -0
  261. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/profile.move +162 -0
  262. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pumpkin.move +136 -0
  263. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pumpkin_seed.move +80 -0
  264. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/rare_egg.move +136 -0
  265. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/scarecrow.move +64 -0
  266. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/season_config.move +197 -0
  267. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/season_stats.move +64 -0
  268. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/seasonal_egg.move +136 -0
  269. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/shop_config.move +267 -0
  270. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/trophy.move +245 -0
  271. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/wheat.move +136 -0
  272. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/wheat_seed.move +80 -0
  273. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/world_permit_id.move +64 -0
  274. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/user_storage_init.move +26 -0
  275. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/scripts/deploy_hook.move +45 -0
  276. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/scripts/migrate.move +7 -0
  277. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/crow_system.move +96 -0
  278. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/farm_system.move +182 -0
  279. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/market_system.move +172 -0
  280. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/pet_system.move +495 -0
  281. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/season_system.move +70 -0
  282. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/shop_system.move +112 -0
  283. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/world_system.move +34 -0
  284. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/tests/farm_tests.move +1192 -0
  285. package/templates/nextjs/sui-farm/pnpm-workspace.yaml +2 -0
  286. package/templates/nextjs/sui-template/packages/client/src/app/components/ProxyCard.tsx +683 -0
  287. package/templates/nextjs/sui-template/packages/client/src/app/page.tsx +510 -5
  288. package/templates/nextjs/sui-template/packages/client/src/app/providers.tsx +13 -5
  289. package/templates/nextjs/sui-template/packages/contracts/Pub.localnet.toml +4 -4
  290. package/templates/nextjs/sui-template/packages/contracts/counter.lock.json +14 -0
  291. package/templates/nextjs/sui-template/packages/contracts/deployment.ts +8 -3
  292. package/templates/nextjs/sui-template/packages/contracts/dubhe.config.json +9 -9
  293. package/templates/nextjs/sui-template/packages/contracts/dubhe.config.ts +0 -4
  294. package/templates/nextjs/sui-template/packages/contracts/metadata.json +180 -124
  295. package/templates/nextjs/sui-template/packages/contracts/package.json +8 -4
  296. package/templates/nextjs/sui-template/packages/contracts/src/counter/Move.toml +4 -0
  297. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/dapp_key.move +45 -31
  298. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/error.move +10 -0
  299. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/genesis.move +30 -13
  300. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/init_test.move +19 -14
  301. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/resources/counter1.move +30 -20
  302. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/resources/counter2.move +35 -24
  303. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/user_storage_init.move +26 -0
  304. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/scripts/deploy_hook.move +6 -10
  305. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/systems/counter.move +7 -20
  306. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/tests/counter.move +14 -27
  307. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/Move.lock +33 -30
  308. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/Move.toml +8 -3
  309. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/dapp_key.move +45 -31
  310. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/error.move +170 -0
  311. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/genesis.move +16 -13
  312. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/init_test.move +8 -14
  313. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/account_key.move +1 -3
  314. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/dapp_service.move +1776 -137
  315. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/data_key.move +1 -20
  316. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/events.move +864 -25
  317. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/scripts/deploy_hook.move +15 -16
  318. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/scripts/migrate.move +0 -3
  319. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/address_system.move +25 -17
  320. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/dapp_system.move +2802 -343
  321. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/utils.move +1 -1
  322. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/address.move +173 -55
  323. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/dapp_test.move +769 -0
  324. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/fee_test.move +2965 -0
  325. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/integration_test.move +303 -0
  326. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/listing_test.move +1422 -0
  327. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/scene_session_test.move +269 -0
  328. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/session_test.move +624 -0
  329. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/storage_test.move +700 -0
  330. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/typed_object_test.move +177 -0
  331. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/typed_scene_test.move +452 -0
  332. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/user_storage_test.move +160 -0
  333. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/utils/math.move +4 -3
  334. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/utils/type_info.move +2 -2
  335. package/templates/101/sui-template/packages/contracts/src/counter/Move.lock +0 -41
  336. package/templates/101/sui-template/packages/contracts/src/counter/Published.toml +0 -9
  337. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/errors.move +0 -12
  338. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/errors.move +0 -140
  339. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_config.move +0 -186
  340. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_state.move +0 -306
  341. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_metadata.move +0 -396
  342. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dubhe_config.move +0 -246
  343. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/dapp_store.move +0 -29
  344. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/session_system.move +0 -44
  345. package/templates/contract/sui-template/src/dubhe/sources/codegen/errors.move +0 -140
  346. package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dapp_fee_config.move +0 -186
  347. package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dapp_fee_state.move +0 -306
  348. package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dapp_metadata.move +0 -396
  349. package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dubhe_config.move +0 -246
  350. package/templates/contract/sui-template/src/dubhe/sources/core/dapp_store.move +0 -29
  351. package/templates/contract/sui-template/src/dubhe/sources/systems/session_system.move +0 -44
  352. package/templates/nextjs/sui-template/packages/contracts/src/counter/Move.lock +0 -47
  353. package/templates/nextjs/sui-template/packages/contracts/src/counter/Published.toml +0 -9
  354. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/errors.move +0 -12
  355. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/errors.move +0 -140
  356. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_config.move +0 -186
  357. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_state.move +0 -306
  358. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_metadata.move +0 -396
  359. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dubhe_config.move +0 -246
  360. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/dapp_store.move +0 -29
  361. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/session_system.move +0 -44
@@ -0,0 +1,1422 @@
1
+ /// Unit tests — Listing market protocol
2
+ ///
3
+ /// Covers:
4
+ /// - new_listing / destroy_listing / share_listing
5
+ /// - is_listing_expired
6
+ /// - take_record / restore_record (cancel / buy listing)
7
+ /// - take_fungible_record (partial fungible listing)
8
+ /// - buy_record (unique item purchase — buyer != seller)
9
+ /// - buy_fungible_record (fungible purchase — adds to buyer balance)
10
+ /// - restore_record by non-seller aborts
11
+ /// - restore_record on fungible listing aborts (must use cancel_fungible_listing)
12
+ /// - expire_listing (anyone can call once expired)
13
+ /// - expire_listing cross-DApp seller_storage aborts
14
+ /// - cancel_fungible_listing (ADDS listed amount back — no overwrite)
15
+ /// - expire_fungible_listing (ADDS listed amount back — no overwrite)
16
+ /// - buy_record self-trade aborts (buyer == seller)
17
+ /// - buy_fungible_record self-trade aborts (buyer == seller)
18
+ /// - update_marketplace_dapp_share: non-admin aborts
19
+ /// - update_marketplace_dapp_share: bps > 10_000 aborts
20
+ /// - settle_marketplace_fee: dapp_key mismatch aborts
21
+ /// - settle_marketplace_fee: all-to-dapp when share_bps == 10_000
22
+ /// - settle_marketplace_fee: all-to-framework when share_bps == 0
23
+ /// - buy_record aborts when DApp is paused
24
+ /// - buy_fungible_record aborts when DApp is paused
25
+ /// - settle_marketplace_fee aborts when DApp is paused
26
+ #[test_only]
27
+ module dubhe::listing_test;
28
+
29
+ use dubhe::dapp_service::{Self, UserStorage};
30
+ use dubhe::dapp_system;
31
+ use sui::bcs::to_bytes;
32
+ use sui::sui::SUI;
33
+
34
+ public struct ListKey has copy, drop {}
35
+ /// A second DApp key used to test cross-DApp rejection.
36
+ public struct OtherDappKey has copy, drop {}
37
+
38
+ // ─── Module-level constants for security tests ────────────────────────────────
39
+ const LISTING_OWNER: address = @0xA1;
40
+ const LISTING_SESSION: address = @0xA2;
41
+ const LISTING_ADMIN: address = @0xC1;
42
+
43
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
44
+
45
+ fun make_us(owner: address, ctx: &mut TxContext): UserStorage {
46
+ dapp_service::create_user_storage_for_testing<ListKey>(owner, ctx)
47
+ }
48
+
49
+ fun weapon_key(item_id: u64): vector<vector<u8>> {
50
+ vector[b"weapon", to_bytes(&item_id)]
51
+ }
52
+ fun weapon_fields(): vector<vector<u8>> { vector[b"damage", b"rarity"] }
53
+ fun weapon_values(dmg: u64, rar: u8): vector<vector<u8>> {
54
+ vector[to_bytes(&dmg), to_bytes(&rar)]
55
+ }
56
+
57
+ fun gold_key(): vector<vector<u8>> { vector[b"gold"] }
58
+
59
+ fun set_gold(us: &mut UserStorage, amount: u64, ctx: &mut TxContext) {
60
+ dapp_system::set_record<ListKey>(
61
+ ListKey {},
62
+ us,
63
+ gold_key(),
64
+ vector[b"amount"],
65
+ vector[to_bytes(&amount)],
66
+ false,
67
+ ctx,
68
+ );
69
+ }
70
+
71
+ fun read_gold(us: &UserStorage): u64 {
72
+ let bytes = dapp_service::get_user_field<ListKey>(us, gold_key(), b"amount");
73
+ let mut bcs = sui::bcs::new(bytes);
74
+ bcs.peel_u64()
75
+ }
76
+
77
+ // ─── is_listing_expired ───────────────────────────────────────────────────────
78
+
79
+ #[test]
80
+ fun test_listing_not_expired_with_none() {
81
+ let mut ctx = sui::tx_context::dummy();
82
+ let seller = ctx.sender();
83
+ let us = make_us(seller, &mut ctx);
84
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&us);
85
+
86
+ let listing = dapp_service::new_listing<SUI>(
87
+ vector[],
88
+ b"weapon",
89
+ weapon_key(1),
90
+ weapon_fields(),
91
+ seller,
92
+ 100,
93
+ std::option::none(),
94
+ dapp_key_str,
95
+ false, // is_fungible
96
+ &mut ctx,
97
+ );
98
+
99
+ assert!(!dapp_service::is_listing_expired(&listing, 999_999_999), 0);
100
+
101
+ let (_, _, _, _, _, _, _, _) = dapp_service::destroy_listing(listing);
102
+ dapp_service::destroy_user_storage(us);
103
+ }
104
+
105
+ #[test]
106
+ fun test_listing_expired_past_deadline() {
107
+ let mut ctx = sui::tx_context::dummy();
108
+ let seller = ctx.sender();
109
+ let us = make_us(seller, &mut ctx);
110
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&us);
111
+
112
+ let listing = dapp_service::new_listing<SUI>(
113
+ vector[],
114
+ b"weapon",
115
+ weapon_key(2),
116
+ weapon_fields(),
117
+ seller,
118
+ 200,
119
+ std::option::some(1_000_000u64),
120
+ dapp_key_str,
121
+ false, // is_fungible
122
+ &mut ctx,
123
+ );
124
+
125
+ assert!(!dapp_service::is_listing_expired(&listing, 999_999), 0);
126
+ assert!(dapp_service::is_listing_expired(&listing, 1_000_000), 1);
127
+ assert!(dapp_service::is_listing_expired(&listing, 1_000_001), 2);
128
+
129
+ let (_, _, _, _, _, _, _, _) = dapp_service::destroy_listing(listing);
130
+ dapp_service::destroy_user_storage(us);
131
+ }
132
+
133
+ // ─── take_record / restore_record (cancel listing) ────────────────────────────#[test]
134
+ fun test_take_record_removes_from_storage() {
135
+ let mut ctx = sui::tx_context::dummy();
136
+ let seller = ctx.sender();
137
+ let mut us = make_us(seller, &mut ctx);
138
+
139
+ dapp_system::set_record<ListKey>(
140
+ ListKey {},
141
+ &mut us,
142
+ weapon_key(10),
143
+ weapon_fields(),
144
+ weapon_values(500, 3),
145
+ false,
146
+ &mut ctx,
147
+ );
148
+ assert!(dapp_service::has_user_record<ListKey>(&us, weapon_key(10)), 0);
149
+
150
+ dapp_system::take_record<ListKey, SUI>(
151
+ ListKey {},
152
+ &mut us,
153
+ b"weapon",
154
+ weapon_key(10),
155
+ weapon_fields(),
156
+ 500,
157
+ std::option::none(),
158
+ &mut ctx,
159
+ );
160
+
161
+ // Record is removed from user storage after take.
162
+ assert!(!dapp_service::has_user_record<ListKey>(&us, weapon_key(10)), 1);
163
+
164
+ dapp_service::destroy_user_storage(us);
165
+ }
166
+
167
+ // ─── expire_listing: must be past deadline ───────────────────────────────────
168
+
169
+ #[test]
170
+ fun test_expire_listing_past_deadline() {
171
+ let mut ctx = sui::tx_context::dummy();
172
+ let seller = ctx.sender();
173
+ let mut us = make_us(seller, &mut ctx);
174
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&us);
175
+
176
+ // dummy() epoch_timestamp_ms = 0; listed_until = 0 means already expired.
177
+ let listing = dapp_service::new_listing<SUI>(
178
+ weapon_values(200, 2),
179
+ b"weapon",
180
+ weapon_key(20),
181
+ weapon_fields(),
182
+ seller,
183
+ 100,
184
+ std::option::some(0u64),
185
+ dapp_key_str,
186
+ false, // is_fungible
187
+ &mut ctx,
188
+ );
189
+
190
+ dapp_system::expire_listing<ListKey, SUI>(ListKey {}, listing, &mut us, &ctx);
191
+
192
+ assert!(dapp_service::has_user_record<ListKey>(&us, weapon_key(20)), 0);
193
+
194
+ dapp_service::destroy_user_storage(us);
195
+ }
196
+
197
+ // ─── restore_record: seller cancels listing ────────────────────────────────────
198
+
199
+ #[test]
200
+ fun test_restore_record_returns_item_to_seller() {
201
+ let mut ctx = sui::tx_context::dummy();
202
+ let seller = ctx.sender();
203
+ let mut us = make_us(seller, &mut ctx);
204
+
205
+ // Put item in user storage.
206
+ dapp_system::set_record<ListKey>(
207
+ ListKey {},
208
+ &mut us,
209
+ weapon_key(50),
210
+ weapon_fields(),
211
+ weapon_values(800, 5),
212
+ false,
213
+ &mut ctx,
214
+ );
215
+ assert!(dapp_service::has_user_record<ListKey>(&us, weapon_key(50)), 0);
216
+
217
+ // List it — removes from user storage.
218
+ dapp_system::take_record<ListKey, SUI>(
219
+ ListKey {},
220
+ &mut us,
221
+ b"weapon",
222
+ weapon_key(50),
223
+ weapon_fields(),
224
+ 200,
225
+ std::option::none(),
226
+ &mut ctx,
227
+ );
228
+ assert!(!dapp_service::has_user_record<ListKey>(&us, weapon_key(50)), 1);
229
+
230
+ // Reconstruct a Listing manually (simulating what take_record shared).
231
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&us);
232
+ let listing = dapp_service::new_listing<SUI>(
233
+ weapon_values(800, 5),
234
+ b"weapon",
235
+ weapon_key(50),
236
+ weapon_fields(),
237
+ seller,
238
+ 200,
239
+ std::option::none(),
240
+ dapp_key_str,
241
+ false, // is_fungible
242
+ &mut ctx,
243
+ );
244
+
245
+ // Cancel: seller restores the item.
246
+ dapp_system::restore_record<ListKey, SUI>(ListKey {}, listing, &mut us, &ctx);
247
+
248
+ // Item is back.
249
+ assert!(dapp_service::has_user_record<ListKey>(&us, weapon_key(50)), 2);
250
+
251
+ dapp_service::destroy_user_storage(us);
252
+ }
253
+
254
+ #[test]
255
+ #[expected_failure]
256
+ fun test_restore_record_non_seller_aborts() {
257
+ let mut ctx = sui::tx_context::dummy();
258
+ let seller = ctx.sender(); // @0x0
259
+ let mut us = make_us(seller, &mut ctx);
260
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&us);
261
+
262
+ // Listing is owned by seller (@0x0).
263
+ let listing = dapp_service::new_listing<SUI>(
264
+ vector[],
265
+ b"weapon",
266
+ weapon_key(60),
267
+ weapon_fields(),
268
+ @0xABCD, // different seller
269
+ 100,
270
+ std::option::none(),
271
+ dapp_key_str,
272
+ false, // is_fungible
273
+ &mut ctx,
274
+ );
275
+
276
+ // ctx.sender() == @0x0 but listing.seller == @0xABCD — must abort.
277
+ dapp_system::restore_record<ListKey, SUI>(ListKey {}, listing, &mut us, &ctx);
278
+
279
+ dapp_service::destroy_user_storage(us);
280
+ }
281
+
282
+ #[test]
283
+ #[expected_failure]
284
+ fun test_expire_listing_not_yet_expired_aborts() {
285
+ let mut ctx = sui::tx_context::dummy();
286
+ let seller = ctx.sender();
287
+ let mut us = make_us(seller, &mut ctx);
288
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&us);
289
+
290
+ let listing = dapp_service::new_listing<SUI>(
291
+ vector[],
292
+ b"weapon",
293
+ weapon_key(30),
294
+ weapon_fields(),
295
+ seller,
296
+ 100,
297
+ std::option::some(999_999_999_999u64),
298
+ dapp_key_str,
299
+ false, // is_fungible
300
+ &mut ctx,
301
+ );
302
+
303
+ dapp_system::expire_listing<ListKey, SUI>(ListKey {}, listing, &mut us, &ctx);
304
+
305
+ dapp_service::destroy_user_storage(us);
306
+ }
307
+
308
+ // ─── take_fungible_record ─────────────────────────────────────────────────────
309
+
310
+ #[test]
311
+ fun test_take_fungible_record_partial_listing() {
312
+ let mut ctx = sui::tx_context::dummy();
313
+ let seller = ctx.sender();
314
+ let mut us = make_us(seller, &mut ctx);
315
+
316
+ // Give seller 300 gold.
317
+ set_gold(&mut us, 300, &mut ctx);
318
+ assert!(read_gold(&us) == 300, 0);
319
+
320
+ // List only 100 gold.
321
+ dapp_system::take_fungible_record<ListKey, SUI>(
322
+ ListKey {},
323
+ &mut us,
324
+ b"gold",
325
+ gold_key(),
326
+ b"amount",
327
+ 100,
328
+ 50,
329
+ std::option::none(),
330
+ &mut ctx,
331
+ );
332
+
333
+ // Seller should still have 200 gold.
334
+ assert!(read_gold(&us) == 200, 1);
335
+
336
+ dapp_service::destroy_user_storage(us);
337
+ }
338
+
339
+ #[test]
340
+ fun test_take_fungible_record_exact_balance_deletes_record() {
341
+ let mut ctx = sui::tx_context::dummy();
342
+ let seller = ctx.sender();
343
+ let mut us = make_us(seller, &mut ctx);
344
+
345
+ set_gold(&mut us, 50, &mut ctx);
346
+
347
+ // List entire balance — record should be deleted.
348
+ dapp_system::take_fungible_record<ListKey, SUI>(
349
+ ListKey {},
350
+ &mut us,
351
+ b"gold",
352
+ gold_key(),
353
+ b"amount",
354
+ 50,
355
+ 10,
356
+ std::option::none(),
357
+ &mut ctx,
358
+ );
359
+
360
+ assert!(!dapp_service::has_user_record<ListKey>(&us, gold_key()), 0);
361
+ dapp_service::destroy_user_storage(us);
362
+ }
363
+
364
+ #[test]
365
+ #[expected_failure]
366
+ fun test_take_fungible_record_insufficient_balance_aborts() {
367
+ let mut ctx = sui::tx_context::dummy();
368
+ let seller = ctx.sender();
369
+ let mut us = make_us(seller, &mut ctx);
370
+
371
+ set_gold(&mut us, 50, &mut ctx);
372
+
373
+ // Try to list 100 gold but only 50 available — must abort.
374
+ dapp_system::take_fungible_record<ListKey, SUI>(
375
+ ListKey {},
376
+ &mut us,
377
+ b"gold",
378
+ gold_key(),
379
+ b"amount",
380
+ 100,
381
+ 10,
382
+ std::option::none(),
383
+ &mut ctx,
384
+ );
385
+ dapp_service::destroy_user_storage(us);
386
+ }
387
+
388
+ // ─── buy_record (unique item purchase) ───────────────────────────────────────
389
+
390
+ #[test]
391
+ fun test_buy_record_transfers_item_to_buyer() {
392
+ let mut ctx = sui::tx_context::dummy();
393
+ let seller = ctx.sender(); // @0x0 — also acts as buyer in this test
394
+ let mut buyer_us = make_us(seller, &mut ctx);
395
+ let dh = dapp_service::create_dapp_hub_for_testing(&mut ctx);
396
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(&mut ctx);
397
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&buyer_us);
398
+
399
+ // Buyer does NOT have weapon #99 yet.
400
+ assert!(!dapp_service::has_user_record<ListKey>(&buyer_us, weapon_key(99)), 0);
401
+
402
+ // Listing for weapon #99 from a different seller (@0x1234).
403
+ let listing = dapp_service::new_listing<SUI>(
404
+ weapon_values(1000, 5),
405
+ b"weapon",
406
+ weapon_key(99),
407
+ weapon_fields(),
408
+ @0x1234, // different seller — prevents self-trade abort
409
+ 500,
410
+ std::option::none(),
411
+ dapp_key_str,
412
+ false, // is_fungible
413
+ &mut ctx,
414
+ );
415
+
416
+ // Payment must cover the listing price (500).
417
+ let payment = sui::coin::mint_for_testing<SUI>(500, &mut ctx);
418
+ let change = dapp_system::buy_record<ListKey, SUI>(
419
+ ListKey {}, &dh, &mut ds, listing, &mut buyer_us, payment, &mut ctx
420
+ );
421
+ // Weapon #99 is now in buyer's storage.
422
+ assert!(dapp_service::has_user_record<ListKey>(&buyer_us, weapon_key(99)), 1);
423
+
424
+ sui::coin::burn_for_testing(change);
425
+ dapp_service::destroy_user_storage(buyer_us);
426
+ dapp_service::destroy_dapp_hub(dh);
427
+ dapp_service::destroy_dapp_storage(ds);
428
+ }
429
+
430
+ // ─── buy_fungible_record ──────────────────────────────────────────────────────
431
+
432
+ #[test]
433
+ fun test_buy_fungible_record_adds_to_existing_balance() {
434
+ let mut ctx = sui::tx_context::dummy();
435
+ let buyer = ctx.sender(); // @0x0
436
+ let mut buyer_us = make_us(buyer, &mut ctx);
437
+ let dh = dapp_service::create_dapp_hub_for_testing(&mut ctx);
438
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(&mut ctx);
439
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&buyer_us);
440
+
441
+ // Buyer already has 200 gold.
442
+ set_gold(&mut buyer_us, 200, &mut ctx);
443
+
444
+ // Create a listing for 75 gold at price 10 SUI.
445
+ let record_values = vector[to_bytes(&75u64)];
446
+ let listing = dapp_service::new_listing<SUI>(
447
+ record_values,
448
+ b"gold",
449
+ gold_key(),
450
+ vector[b"amount"],
451
+ @0xABCD, // seller is someone else
452
+ 10,
453
+ std::option::none(),
454
+ dapp_key_str,
455
+ true, // is_fungible
456
+ &mut ctx,
457
+ );
458
+
459
+ // Buy: buyer already has 200, adding 75 → should be 275.
460
+ let payment = sui::coin::mint_for_testing<SUI>(10, &mut ctx);
461
+ let change = dapp_system::buy_fungible_record<ListKey, SUI>(
462
+ ListKey {}, &dh, &mut ds, listing, &mut buyer_us, payment, &mut ctx
463
+ );
464
+ assert!(read_gold(&buyer_us) == 275, 0);
465
+
466
+ sui::coin::burn_for_testing(change);
467
+ dapp_service::destroy_user_storage(buyer_us);
468
+ dapp_service::destroy_dapp_hub(dh);
469
+ dapp_service::destroy_dapp_storage(ds);
470
+ }
471
+
472
+ #[test]
473
+ fun test_buy_fungible_record_creates_record_if_buyer_has_none() {
474
+ let mut ctx = sui::tx_context::dummy();
475
+ let buyer = ctx.sender();
476
+ let mut buyer_us = make_us(buyer, &mut ctx);
477
+ let dh = dapp_service::create_dapp_hub_for_testing(&mut ctx);
478
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(&mut ctx);
479
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&buyer_us);
480
+
481
+ // Buyer has NO gold yet.
482
+ assert!(!dapp_service::has_user_record<ListKey>(&buyer_us, gold_key()), 0);
483
+
484
+ let record_values = vector[to_bytes(&100u64)];
485
+ let listing = dapp_service::new_listing<SUI>(
486
+ record_values,
487
+ b"gold",
488
+ gold_key(),
489
+ vector[b"amount"],
490
+ @0xABCD,
491
+ 5,
492
+ std::option::none(),
493
+ dapp_key_str,
494
+ true, // is_fungible
495
+ &mut ctx,
496
+ );
497
+
498
+ let payment = sui::coin::mint_for_testing<SUI>(5, &mut ctx);
499
+ let change = dapp_system::buy_fungible_record<ListKey, SUI>(
500
+ ListKey {}, &dh, &mut ds, listing, &mut buyer_us, payment, &mut ctx
501
+ );
502
+ // Should have exactly 100 gold now.
503
+ assert!(read_gold(&buyer_us) == 100, 1);
504
+
505
+ sui::coin::burn_for_testing(change);
506
+ dapp_service::destroy_user_storage(buyer_us);
507
+ dapp_service::destroy_dapp_hub(dh);
508
+ dapp_service::destroy_dapp_storage(ds);
509
+ }
510
+
511
+ #[test]
512
+ #[expected_failure]
513
+ fun test_buy_record_expired_listing_aborts() {
514
+ let mut ctx = sui::tx_context::dummy();
515
+ let buyer = ctx.sender();
516
+ let mut buyer_us = make_us(buyer, &mut ctx);
517
+ let dh = dapp_service::create_dapp_hub_for_testing(&mut ctx);
518
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(&mut ctx);
519
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&buyer_us);
520
+
521
+ // dummy() epoch_timestamp_ms = 0; listed_until = 0 → already expired.
522
+ let listing = dapp_service::new_listing<SUI>(
523
+ weapon_values(100, 1),
524
+ b"weapon",
525
+ weapon_key(1),
526
+ weapon_fields(),
527
+ @0xABCD,
528
+ 100,
529
+ std::option::some(0u64),
530
+ dapp_key_str,
531
+ false, // is_fungible
532
+ &mut ctx,
533
+ );
534
+
535
+ let payment = sui::coin::mint_for_testing<SUI>(100, &mut ctx);
536
+ let change = dapp_system::buy_record<ListKey, SUI>(
537
+ ListKey {}, &dh, &mut ds, listing, &mut buyer_us, payment, &mut ctx
538
+ );
539
+ sui::coin::burn_for_testing(change);
540
+ dapp_service::destroy_user_storage(buyer_us);
541
+ dapp_service::destroy_dapp_hub(dh);
542
+ dapp_service::destroy_dapp_storage(ds);
543
+ }
544
+
545
+ // Supplying less than the listing price must abort.
546
+ #[test]
547
+ #[expected_failure(abort_code = dubhe::error::EInsufficientPayment)]
548
+ fun test_buy_record_insufficient_payment_aborts() {
549
+ let mut ctx = sui::tx_context::dummy();
550
+ let buyer = ctx.sender();
551
+ let mut buyer_us = make_us(buyer, &mut ctx);
552
+ let dh = dapp_service::create_dapp_hub_for_testing(&mut ctx);
553
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(&mut ctx);
554
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&buyer_us);
555
+
556
+ let listing = dapp_service::new_listing<SUI>(
557
+ weapon_values(100, 1),
558
+ b"weapon",
559
+ weapon_key(1),
560
+ weapon_fields(),
561
+ @0xABCD,
562
+ 500, // price = 500
563
+ std::option::none(),
564
+ dapp_key_str,
565
+ false,
566
+ &mut ctx,
567
+ );
568
+
569
+ // Only send 499 — must abort.
570
+ let payment = sui::coin::mint_for_testing<SUI>(499, &mut ctx);
571
+ let change = dapp_system::buy_record<ListKey, SUI>(
572
+ ListKey {}, &dh, &mut ds, listing, &mut buyer_us, payment, &mut ctx
573
+ );
574
+ sui::coin::burn_for_testing(change);
575
+ dapp_service::destroy_user_storage(buyer_us);
576
+ dapp_service::destroy_dapp_hub(dh);
577
+ dapp_service::destroy_dapp_storage(ds);
578
+ }
579
+
580
+ // Paying more than the price returns correct change.
581
+ #[test]
582
+ fun test_buy_record_overpayment_returns_change() {
583
+ let mut ctx = sui::tx_context::dummy();
584
+ let buyer = ctx.sender();
585
+ let mut buyer_us = make_us(buyer, &mut ctx);
586
+ let dh = dapp_service::create_dapp_hub_for_testing(&mut ctx);
587
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(&mut ctx);
588
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&buyer_us);
589
+
590
+ let listing = dapp_service::new_listing<SUI>(
591
+ weapon_values(100, 1),
592
+ b"weapon",
593
+ weapon_key(5),
594
+ weapon_fields(),
595
+ @0xABCD, // different seller
596
+ 300, // price = 300
597
+ std::option::none(),
598
+ dapp_key_str,
599
+ false,
600
+ &mut ctx,
601
+ );
602
+
603
+ // Send 1000 — change should be 700.
604
+ let payment = sui::coin::mint_for_testing<SUI>(1000, &mut ctx);
605
+ let change = dapp_system::buy_record<ListKey, SUI>(
606
+ ListKey {}, &dh, &mut ds, listing, &mut buyer_us, payment, &mut ctx
607
+ );
608
+ assert!(sui::coin::value(&change) == 700, 0);
609
+
610
+ sui::coin::burn_for_testing(change);
611
+ dapp_service::destroy_user_storage(buyer_us);
612
+ dapp_service::destroy_dapp_hub(dh);
613
+ dapp_service::destroy_dapp_storage(ds);
614
+ }
615
+
616
+ // ─── cancel_fungible_listing: ADDS back (not overwrite) ─────────────────────
617
+
618
+ #[test]
619
+ fun test_cancel_fungible_listing_adds_to_existing_balance() {
620
+ let mut ctx = sui::tx_context::dummy();
621
+ let seller = ctx.sender();
622
+ let mut us = make_us(seller, &mut ctx);
623
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&us);
624
+
625
+ // Seller lists 100 gold (balance decreases from 300 to 200).
626
+ set_gold(&mut us, 300, &mut ctx);
627
+ dapp_system::take_fungible_record<ListKey, SUI>(
628
+ ListKey {},
629
+ &mut us,
630
+ b"gold",
631
+ gold_key(),
632
+ b"amount",
633
+ 100,
634
+ 50,
635
+ std::option::none(),
636
+ &mut ctx,
637
+ );
638
+ assert!(read_gold(&us) == 200, 0);
639
+
640
+ // Seller later earns another 50 gold while listing is live.
641
+ set_gold(&mut us, 250, &mut ctx); // directly overwrite to 250
642
+
643
+ // Reconstruct the Listing for the 100 gold that was taken.
644
+ let record_values = vector[to_bytes(&100u64)];
645
+ let listing = dapp_service::new_listing<SUI>(
646
+ record_values,
647
+ b"gold",
648
+ gold_key(),
649
+ vector[b"amount"],
650
+ seller,
651
+ 50,
652
+ std::option::none(),
653
+ dapp_key_str,
654
+ true, // is_fungible
655
+ &mut ctx,
656
+ );
657
+
658
+ // Cancel: should ADD 100 back to existing 250, NOT overwrite with 100.
659
+ dapp_system::cancel_fungible_listing<ListKey, SUI>(ListKey {}, listing, &mut us, &ctx);
660
+
661
+ // 250 + 100 = 350, not 100.
662
+ assert!(read_gold(&us) == 350, 1);
663
+
664
+ dapp_service::destroy_user_storage(us);
665
+ }
666
+
667
+ #[test]
668
+ fun test_cancel_fungible_listing_creates_record_if_none_exists() {
669
+ let mut ctx = sui::tx_context::dummy();
670
+ let seller = ctx.sender();
671
+ let mut us = make_us(seller, &mut ctx);
672
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&us);
673
+
674
+ // Seller has no gold record at all.
675
+ assert!(!dapp_service::has_user_record<ListKey>(&us, gold_key()), 0);
676
+
677
+ // Build listing for 60 gold.
678
+ let record_values = vector[to_bytes(&60u64)];
679
+ let listing = dapp_service::new_listing<SUI>(
680
+ record_values,
681
+ b"gold",
682
+ gold_key(),
683
+ vector[b"amount"],
684
+ seller,
685
+ 5,
686
+ std::option::none(),
687
+ dapp_key_str,
688
+ true, // is_fungible
689
+ &mut ctx,
690
+ );
691
+
692
+ dapp_system::cancel_fungible_listing<ListKey, SUI>(ListKey {}, listing, &mut us, &ctx);
693
+
694
+ // Seller should now have 60 gold.
695
+ assert!(read_gold(&us) == 60, 1);
696
+
697
+ dapp_service::destroy_user_storage(us);
698
+ }
699
+
700
+ #[test]
701
+ #[expected_failure]
702
+ fun test_cancel_fungible_listing_non_seller_aborts() {
703
+ let mut ctx = sui::tx_context::dummy();
704
+ let seller = ctx.sender(); // @0x0
705
+ let mut us = make_us(seller, &mut ctx);
706
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&us);
707
+
708
+ let record_values = vector[to_bytes(&50u64)];
709
+ let listing = dapp_service::new_listing<SUI>(
710
+ record_values,
711
+ b"gold",
712
+ gold_key(),
713
+ vector[b"amount"],
714
+ @0xDEAD, // different seller
715
+ 5,
716
+ std::option::none(),
717
+ dapp_key_str,
718
+ true, // is_fungible
719
+ &mut ctx,
720
+ );
721
+
722
+ // ctx.sender() == @0x0 but listing.seller == @0xDEAD — must abort.
723
+ dapp_system::cancel_fungible_listing<ListKey, SUI>(ListKey {}, listing, &mut us, &ctx);
724
+
725
+ dapp_service::destroy_user_storage(us);
726
+ }
727
+
728
+ // ─── expire_fungible_listing: ADDS back (not overwrite) ─────────────────────
729
+
730
+ #[test]
731
+ fun test_expire_fungible_listing_adds_to_existing_balance() {
732
+ let mut ctx = sui::tx_context::dummy();
733
+ let seller = ctx.sender();
734
+ let mut us = make_us(seller, &mut ctx);
735
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&us);
736
+
737
+ // Seller has 400 gold when listing expires.
738
+ set_gold(&mut us, 400, &mut ctx);
739
+
740
+ // Build an already-expired listing for 80 gold.
741
+ let record_values = vector[to_bytes(&80u64)];
742
+ let listing = dapp_service::new_listing<SUI>(
743
+ record_values,
744
+ b"gold",
745
+ gold_key(),
746
+ vector[b"amount"],
747
+ seller,
748
+ 10,
749
+ std::option::some(0u64), // already expired (epoch_timestamp_ms = 0 in dummy ctx)
750
+ dapp_key_str,
751
+ true, // is_fungible
752
+ &mut ctx,
753
+ );
754
+
755
+ // Expire: should ADD 80 back to existing 400, NOT overwrite with 80.
756
+ dapp_system::expire_fungible_listing<ListKey, SUI>(ListKey {}, listing, &mut us, &ctx);
757
+
758
+ // 400 + 80 = 480, not 80.
759
+ assert!(read_gold(&us) == 480, 0);
760
+
761
+ dapp_service::destroy_user_storage(us);
762
+ }
763
+
764
+ #[test]
765
+ fun test_expire_fungible_listing_creates_record_if_none_exists() {
766
+ let mut ctx = sui::tx_context::dummy();
767
+ let seller = ctx.sender();
768
+ let mut us = make_us(seller, &mut ctx);
769
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&us);
770
+
771
+ // Seller has no gold record.
772
+ assert!(!dapp_service::has_user_record<ListKey>(&us, gold_key()), 0);
773
+
774
+ let record_values = vector[to_bytes(&120u64)];
775
+ let listing = dapp_service::new_listing<SUI>(
776
+ record_values,
777
+ b"gold",
778
+ gold_key(),
779
+ vector[b"amount"],
780
+ seller,
781
+ 10,
782
+ std::option::some(0u64), // already expired
783
+ dapp_key_str,
784
+ true, // is_fungible
785
+ &mut ctx,
786
+ );
787
+
788
+ dapp_system::expire_fungible_listing<ListKey, SUI>(ListKey {}, listing, &mut us, &ctx);
789
+
790
+ assert!(read_gold(&us) == 120, 1);
791
+
792
+ dapp_service::destroy_user_storage(us);
793
+ }
794
+
795
+ #[test]
796
+ #[expected_failure]
797
+ fun test_expire_fungible_listing_not_expired_aborts() {
798
+ let mut ctx = sui::tx_context::dummy();
799
+ let seller = ctx.sender();
800
+ let mut us = make_us(seller, &mut ctx);
801
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&us);
802
+
803
+ let record_values = vector[to_bytes(&50u64)];
804
+ let listing = dapp_service::new_listing<SUI>(
805
+ record_values,
806
+ b"gold",
807
+ gold_key(),
808
+ vector[b"amount"],
809
+ seller,
810
+ 5,
811
+ std::option::some(999_999_999_999u64), // not expired
812
+ dapp_key_str,
813
+ true, // is_fungible
814
+ &mut ctx,
815
+ );
816
+
817
+ dapp_system::expire_fungible_listing<ListKey, SUI>(ListKey {}, listing, &mut us, &ctx);
818
+
819
+ dapp_service::destroy_user_storage(us);
820
+ }
821
+
822
+ // ─── New: restore_record on fungible listing must abort ───────────────────────
823
+
824
+ #[test]
825
+ #[expected_failure]
826
+ fun test_restore_record_on_fungible_listing_aborts() {
827
+ let mut ctx = sui::tx_context::dummy();
828
+ let seller = ctx.sender();
829
+ let mut us = make_us(seller, &mut ctx);
830
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&us);
831
+
832
+ // Build a fungible listing (is_fungible = true).
833
+ let record_values = vector[to_bytes(&100u64)];
834
+ let listing = dapp_service::new_listing<SUI>(
835
+ record_values,
836
+ b"gold",
837
+ gold_key(),
838
+ vector[b"amount"],
839
+ seller,
840
+ 10,
841
+ std::option::none(),
842
+ dapp_key_str,
843
+ true, // is_fungible
844
+ &mut ctx,
845
+ );
846
+
847
+ // restore_record on a fungible listing must abort.
848
+ // Use cancel_fungible_listing instead.
849
+ dapp_system::restore_record<ListKey, SUI>(ListKey {}, listing, &mut us, &ctx);
850
+
851
+ dapp_service::destroy_user_storage(us);
852
+ }
853
+
854
+ // ─── New: buy_record self-trade must abort ────────────────────────────────────
855
+
856
+ #[test]
857
+ #[expected_failure]
858
+ fun test_buy_record_self_trade_aborts() {
859
+ let mut ctx = sui::tx_context::dummy();
860
+ let seller = ctx.sender(); // @0x0
861
+ let mut us = make_us(seller, &mut ctx);
862
+ let dh = dapp_service::create_dapp_hub_for_testing(&mut ctx);
863
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(&mut ctx);
864
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&us);
865
+
866
+ // Listing where seller == @0x0 (same as ctx.sender()).
867
+ let listing = dapp_service::new_listing<SUI>(
868
+ weapon_values(500, 3),
869
+ b"weapon",
870
+ weapon_key(77),
871
+ weapon_fields(),
872
+ seller, // same as ctx.sender()
873
+ 100,
874
+ std::option::none(),
875
+ dapp_key_str,
876
+ false, // is_fungible
877
+ &mut ctx,
878
+ );
879
+
880
+ // buyer_storage.canonical_owner == seller — must abort with no_permission.
881
+ let payment = sui::coin::mint_for_testing<SUI>(100, &mut ctx);
882
+ let change = dapp_system::buy_record<ListKey, SUI>(
883
+ ListKey {}, &dh, &mut ds, listing, &mut us, payment, &mut ctx
884
+ );
885
+ sui::coin::burn_for_testing(change);
886
+ dapp_service::destroy_user_storage(us);
887
+ dapp_service::destroy_dapp_hub(dh);
888
+ dapp_service::destroy_dapp_storage(ds);
889
+ }
890
+
891
+ // ─── New: buy_fungible_record self-trade must abort ──────────────────────────
892
+
893
+ #[test]
894
+ #[expected_failure]
895
+ fun test_buy_fungible_record_self_trade_aborts() {
896
+ let mut ctx = sui::tx_context::dummy();
897
+ let seller = ctx.sender(); // @0x0
898
+ let mut us = make_us(seller, &mut ctx);
899
+ let dh = dapp_service::create_dapp_hub_for_testing(&mut ctx);
900
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(&mut ctx);
901
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&us);
902
+
903
+ // Fungible listing where seller == @0x0 (same as ctx.sender()).
904
+ let listing = dapp_service::new_listing<SUI>(
905
+ vector[sui::bcs::to_bytes(&100u64)],
906
+ b"gold",
907
+ gold_key(),
908
+ vector[b"amount"],
909
+ seller, // same as ctx.sender()
910
+ 50,
911
+ std::option::none(),
912
+ dapp_key_str,
913
+ true, // is_fungible
914
+ &mut ctx,
915
+ );
916
+
917
+ // buyer_storage.canonical_owner == seller — must abort with no_permission.
918
+ let payment = sui::coin::mint_for_testing<SUI>(50, &mut ctx);
919
+ let change = dapp_system::buy_fungible_record<ListKey, SUI>(
920
+ ListKey {}, &dh, &mut ds, listing, &mut us, payment, &mut ctx
921
+ );
922
+ sui::coin::burn_for_testing(change);
923
+ dapp_service::destroy_user_storage(us);
924
+ dapp_service::destroy_dapp_hub(dh);
925
+ dapp_service::destroy_dapp_storage(ds);
926
+ }
927
+
928
+ // ─── New: expire_listing cross-DApp seller_storage must abort ─────────────────
929
+
930
+ #[test]
931
+ #[expected_failure]
932
+ fun test_expire_listing_cross_dapp_storage_aborts() {
933
+ let mut ctx = sui::tx_context::dummy();
934
+ let seller = ctx.sender(); // @0x0
935
+
936
+ // seller_us belongs to ListKey DApp.
937
+ let mut seller_us = make_us(seller, &mut ctx);
938
+ let list_dapp_key_str = dapp_service::user_storage_dapp_key(&seller_us);
939
+
940
+ // other_us belongs to OtherDappKey DApp (same owner, different DApp).
941
+ let mut other_us = dapp_service::create_user_storage_for_testing<OtherDappKey>(seller, &mut ctx);
942
+
943
+ // Build a listing under ListKey DApp that has already expired.
944
+ let listing = dapp_service::new_listing<SUI>(
945
+ weapon_values(300, 2),
946
+ b"weapon",
947
+ weapon_key(88),
948
+ weapon_fields(),
949
+ seller,
950
+ 50,
951
+ std::option::some(0u64), // already expired
952
+ list_dapp_key_str,
953
+ false, // is_fungible
954
+ &mut ctx,
955
+ );
956
+
957
+ // Pass OtherDappKey's UserStorage — must abort with dapp_key_mismatch.
958
+ dapp_system::expire_listing<ListKey, SUI>(ListKey {}, listing, &mut other_us, &ctx);
959
+
960
+ dapp_service::destroy_user_storage(seller_us);
961
+ dapp_service::destroy_user_storage(other_us);
962
+ }
963
+
964
+ // ─── Marketplace fee tests ──────────────────────────────────────────────────
965
+
966
+ #[test]
967
+ fun test_marketplace_fee_defaults() {
968
+ let ctx = &mut tx_context::dummy();
969
+ let dh = dapp_service::create_dapp_hub_for_testing(ctx);
970
+ let cfg = dapp_service::get_config(&dh);
971
+ // Default: 3% fee, 50/50 split
972
+ assert!(dapp_service::marketplace_fee_bps(cfg) == 300, 0);
973
+ assert!(dapp_service::marketplace_dapp_share_bps(cfg) == 5_000, 0);
974
+ dapp_service::destroy_dapp_hub(dh);
975
+ }
976
+
977
+ #[test]
978
+ fun test_update_marketplace_fee_by_admin() {
979
+ let ctx = &mut tx_context::dummy();
980
+ let mut dh = dapp_service::create_dapp_hub_for_testing(ctx);
981
+ dapp_system::update_marketplace_fee(&mut dh, 200, ctx);
982
+ assert!(dapp_service::marketplace_fee_bps(dapp_service::get_config(&dh)) == 200, 0);
983
+ dapp_service::destroy_dapp_hub(dh);
984
+ }
985
+
986
+ #[test]
987
+ #[expected_failure]
988
+ fun test_update_marketplace_fee_aborts_for_non_admin() {
989
+ let ctx = &mut tx_context::dummy();
990
+ let mut dh = dapp_service::create_dapp_hub_for_testing(ctx);
991
+ let ctx2 = &mut tx_context::new_from_hint(@0xBEEF, 0, 0, 0, 0);
992
+ dapp_system::update_marketplace_fee(&mut dh, 200, ctx2);
993
+ dapp_service::destroy_dapp_hub(dh);
994
+ }
995
+
996
+ #[test]
997
+ #[expected_failure]
998
+ fun test_update_marketplace_fee_aborts_for_over_10000() {
999
+ let ctx = &mut tx_context::dummy();
1000
+ let mut dh = dapp_service::create_dapp_hub_for_testing(ctx);
1001
+ dapp_system::update_marketplace_fee(&mut dh, 10_001, ctx);
1002
+ dapp_service::destroy_dapp_hub(dh);
1003
+ }
1004
+
1005
+ #[test]
1006
+ fun test_update_marketplace_dapp_share_by_admin() {
1007
+ let ctx = &mut tx_context::dummy();
1008
+ let mut dh = dapp_service::create_dapp_hub_for_testing(ctx);
1009
+ dapp_system::update_marketplace_dapp_share(&mut dh, 7_000, ctx);
1010
+ assert!(dapp_service::marketplace_dapp_share_bps(dapp_service::get_config(&dh)) == 7_000, 0);
1011
+ dapp_service::destroy_dapp_hub(dh);
1012
+ }
1013
+
1014
+ #[test]
1015
+ fun test_marketplace_fee_returns_global_rate() {
1016
+ let ctx = &mut tx_context::dummy();
1017
+ let dh = dapp_service::create_dapp_hub_for_testing(ctx);
1018
+ // All DApps share the same global rate; default is 300 bps (3%).
1019
+ let fee = dapp_system::marketplace_fee_bps(&dh);
1020
+ assert!(fee == 300, 0);
1021
+ dapp_service::destroy_dapp_hub(dh);
1022
+ }
1023
+
1024
+ #[test]
1025
+ fun test_settle_marketplace_fee_splits_correctly() {
1026
+ let ctx = &mut tx_context::dummy();
1027
+ let dh = dapp_service::create_dapp_hub_for_testing(ctx);
1028
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(ctx);
1029
+ // fee = 100 SUI, 50/50 split => 50 to framework treasury, 50 to DApp
1030
+ let fee_coin = sui::coin::mint_for_testing<sui::sui::SUI>(100, ctx);
1031
+ dapp_system::settle_marketplace_fee<ListKey, sui::sui::SUI>(
1032
+ ListKey {}, &dh, &mut ds, fee_coin, @0x0, ctx
1033
+ );
1034
+ // DApp revenue should now have 50
1035
+ assert!(dapp_service::dapp_revenue_balance<sui::sui::SUI>(&ds) == 50, 0);
1036
+ dapp_service::destroy_dapp_hub(dh);
1037
+ dapp_service::destroy_dapp_storage(ds);
1038
+ }
1039
+
1040
+ #[test]
1041
+ fun test_settle_marketplace_fee_zero_is_noop() {
1042
+ let ctx = &mut tx_context::dummy();
1043
+ let dh = dapp_service::create_dapp_hub_for_testing(ctx);
1044
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(ctx);
1045
+ let fee_coin = sui::coin::mint_for_testing<sui::sui::SUI>(0, ctx);
1046
+ dapp_system::settle_marketplace_fee<ListKey, sui::sui::SUI>(
1047
+ ListKey {}, &dh, &mut ds, fee_coin, @0x0, ctx
1048
+ );
1049
+ assert!(dapp_service::dapp_revenue_balance<sui::sui::SUI>(&ds) == 0, 0);
1050
+ dapp_service::destroy_dapp_hub(dh);
1051
+ dapp_service::destroy_dapp_storage(ds);
1052
+ }
1053
+
1054
+ // ─── Session key cannot create listings ───────────────────────────────────────
1055
+
1056
+ /// A session key must NOT be able to list items. Listing is a high-privilege
1057
+ /// asset-ownership transfer that requires the canonical wallet owner.
1058
+ #[test]
1059
+ #[expected_failure]
1060
+ fun test_take_record_session_key_aborts() {
1061
+ // Sender is LISTING_SESSION, but UserStorage canonical owner is LISTING_OWNER.
1062
+ let mut ctx = tx_context::new_from_hint(LISTING_SESSION, 0, 0, 0, 0);
1063
+ let mut us = make_us(LISTING_OWNER, &mut ctx);
1064
+ // Activate session key so is_write_authorized would pass — but canonical_owner check won't.
1065
+ dapp_service::set_session_key_for_testing(&mut us, LISTING_SESSION, 9_999_999_999);
1066
+ dapp_system::set_record<ListKey>(
1067
+ ListKey {}, &mut us, weapon_key(1), weapon_fields(), weapon_values(100, 1), false, &mut ctx
1068
+ );
1069
+ // This call must abort: ctx.sender() == SESSION != OWNER (canonical_owner).
1070
+ dapp_system::take_record<ListKey, SUI>(
1071
+ ListKey {}, &mut us, b"weapon", weapon_key(1), weapon_fields(),
1072
+ 500, std::option::none(), &mut ctx
1073
+ );
1074
+ dapp_service::destroy_user_storage(us);
1075
+ }
1076
+
1077
+ /// A session key must NOT be able to list fungible items either.
1078
+ #[test]
1079
+ #[expected_failure]
1080
+ fun test_take_fungible_record_session_key_aborts() {
1081
+ // Simulate a context where the sender is the session key, not the owner.
1082
+ let mut ctx = tx_context::new_from_hint(LISTING_SESSION, 0, 0, 0, 0);
1083
+ // UserStorage belongs to LISTING_OWNER but we call from LISTING_SESSION.
1084
+ let mut us = make_us(LISTING_OWNER, &mut ctx);
1085
+ set_gold(&mut us, 100, &mut ctx);
1086
+ // This call must abort: sender == SESSION != OWNER.
1087
+ dapp_system::take_fungible_record<ListKey, SUI>(
1088
+ ListKey {}, &mut us, b"gold", gold_key(), b"amount",
1089
+ 50, 50, std::option::none(), &mut ctx
1090
+ );
1091
+ dapp_service::destroy_user_storage(us);
1092
+ }
1093
+
1094
+ // ─── buy_record: buyer already owns same key must abort ──────────────────────
1095
+
1096
+ #[test]
1097
+ #[expected_failure(abort_code = dubhe::error::EItemAlreadyOwned)]
1098
+ fun test_buy_record_buyer_already_owns_same_key_aborts() {
1099
+ let mut ctx = sui::tx_context::dummy();
1100
+ let buyer = ctx.sender(); // @0x0
1101
+ let mut buyer_us = make_us(buyer, &mut ctx);
1102
+ let dh = dapp_service::create_dapp_hub_for_testing(&mut ctx);
1103
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(&mut ctx);
1104
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&buyer_us);
1105
+
1106
+ // Buyer already owns weapon #99 in their storage.
1107
+ dapp_system::set_record<ListKey>(
1108
+ ListKey {},
1109
+ &mut buyer_us,
1110
+ weapon_key(99),
1111
+ weapon_fields(),
1112
+ weapon_values(800, 4),
1113
+ false,
1114
+ &mut ctx,
1115
+ );
1116
+ assert!(dapp_service::has_user_record<ListKey>(&buyer_us, weapon_key(99)), 0);
1117
+
1118
+ // A listing for weapon #99 from a different seller.
1119
+ let listing = dapp_service::new_listing<SUI>(
1120
+ weapon_values(500, 3),
1121
+ b"weapon",
1122
+ weapon_key(99),
1123
+ weapon_fields(),
1124
+ @0xABCD, // different seller
1125
+ 300,
1126
+ std::option::none(),
1127
+ dapp_key_str,
1128
+ false, // is_fungible
1129
+ &mut ctx,
1130
+ );
1131
+
1132
+ // Buyer tries to purchase weapon #99 but already owns it — must abort EItemAlreadyOwned.
1133
+ let payment = sui::coin::mint_for_testing<SUI>(300, &mut ctx);
1134
+ let change = dapp_system::buy_record<ListKey, SUI>(
1135
+ ListKey {}, &dh, &mut ds, listing, &mut buyer_us, payment, &mut ctx
1136
+ );
1137
+ sui::coin::burn_for_testing(change);
1138
+ dapp_service::destroy_user_storage(buyer_us);
1139
+ dapp_service::destroy_dapp_hub(dh);
1140
+ dapp_service::destroy_dapp_storage(ds);
1141
+ }
1142
+
1143
+ /// Buyer purchasing a weapon they don't yet own must succeed normally.
1144
+ #[test]
1145
+ fun test_buy_record_buyer_owns_different_key_succeeds() {
1146
+ let mut ctx = sui::tx_context::dummy();
1147
+ let buyer = ctx.sender(); // @0x0
1148
+ let mut buyer_us = make_us(buyer, &mut ctx);
1149
+ let dh = dapp_service::create_dapp_hub_for_testing(&mut ctx);
1150
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(&mut ctx);
1151
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&buyer_us);
1152
+
1153
+ // Buyer already owns weapon #42 (different key).
1154
+ dapp_system::set_record<ListKey>(
1155
+ ListKey {},
1156
+ &mut buyer_us,
1157
+ weapon_key(42),
1158
+ weapon_fields(),
1159
+ weapon_values(100, 1),
1160
+ false,
1161
+ &mut ctx,
1162
+ );
1163
+
1164
+ // Listing is for weapon #99 (different key from what buyer owns).
1165
+ let listing = dapp_service::new_listing<SUI>(
1166
+ weapon_values(500, 3),
1167
+ b"weapon",
1168
+ weapon_key(99),
1169
+ weapon_fields(),
1170
+ @0xABCD, // different seller
1171
+ 200,
1172
+ std::option::none(),
1173
+ dapp_key_str,
1174
+ false,
1175
+ &mut ctx,
1176
+ );
1177
+
1178
+ let payment = sui::coin::mint_for_testing<SUI>(200, &mut ctx);
1179
+ let change = dapp_system::buy_record<ListKey, SUI>(
1180
+ ListKey {}, &dh, &mut ds, listing, &mut buyer_us, payment, &mut ctx
1181
+ );
1182
+ assert!(dapp_service::has_user_record<ListKey>(&buyer_us, weapon_key(99)), 0);
1183
+
1184
+ sui::coin::burn_for_testing(change);
1185
+ dapp_service::destroy_user_storage(buyer_us);
1186
+ dapp_service::destroy_dapp_hub(dh);
1187
+ dapp_service::destroy_dapp_storage(ds);
1188
+ }
1189
+
1190
+ /// Verify that `ensure_not_paused` blocks listing when the DApp is halted.
1191
+ /// (This test exercises the framework-level guard used by generated `list` functions.)
1192
+ #[test]
1193
+ #[expected_failure]
1194
+ fun test_ensure_not_paused_aborts_when_paused() {
1195
+ let mut ctx = tx_context::new_from_hint(LISTING_ADMIN, 0, 0, 0, 0);
1196
+ let dh = dapp_system::create_dapp_hub_for_testing(&mut ctx);
1197
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(&mut ctx);
1198
+ // Admin pauses the DApp.
1199
+ dapp_system::set_paused<ListKey>(&dh, &mut ds, true, &mut ctx);
1200
+ // ensure_not_paused must abort.
1201
+ dapp_system::ensure_not_paused<ListKey>(&ds);
1202
+ dapp_service::destroy_dapp_hub(dh);
1203
+ dapp_service::destroy_dapp_storage(ds);
1204
+ }
1205
+
1206
+ /// Verify that cancel (asset recovery) still works when the DApp is paused.
1207
+ /// cancel_fungible_listing has no ensure_not_paused check — users must always
1208
+ /// be able to recover their assets regardless of DApp state.
1209
+ #[test]
1210
+ fun test_cancel_listing_works_when_paused() {
1211
+ let mut ctx = tx_context::new_from_hint(LISTING_ADMIN, 0, 0, 0, 0);
1212
+ let dh = dapp_system::create_dapp_hub_for_testing(&mut ctx);
1213
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(&mut ctx);
1214
+ let mut us = make_us(LISTING_ADMIN, &mut ctx);
1215
+ set_gold(&mut us, 200, &mut ctx);
1216
+
1217
+ // Create a real listing BEFORE pausing (via take_fungible_record).
1218
+ dapp_system::take_fungible_record<ListKey, SUI>(
1219
+ ListKey {}, &mut us, b"gold", gold_key(), b"amount",
1220
+ 100, 50, std::option::none(), &mut ctx
1221
+ );
1222
+ // Manually reconstruct a Listing with the same structure so we can cancel it.
1223
+ // Use new_listing with properly BCS-encoded record_data matching what take_fungible_record built.
1224
+ let amount_bytes = sui::bcs::to_bytes(&100u64);
1225
+ let record_values = vector[amount_bytes];
1226
+ let dapp_key_str = dapp_service::dapp_storage_dapp_key(&ds);
1227
+ let listing = dapp_service::new_listing<SUI>(
1228
+ record_values,
1229
+ b"gold",
1230
+ gold_key(),
1231
+ vector[b"amount"],
1232
+ LISTING_ADMIN,
1233
+ 50,
1234
+ std::option::none(),
1235
+ dapp_key_str,
1236
+ true,
1237
+ &mut ctx,
1238
+ );
1239
+
1240
+ // Pause the DApp AFTER creating the listing.
1241
+ dapp_system::set_paused<ListKey>(&dh, &mut ds, true, &mut ctx);
1242
+ assert!(dapp_service::dapp_paused(&ds), 0);
1243
+
1244
+ // cancel_fungible_listing must still succeed even while paused.
1245
+ dapp_system::cancel_fungible_listing<ListKey, SUI>(
1246
+ ListKey {}, listing, &mut us, &ctx
1247
+ );
1248
+ // Gold: 200 initial - 100 taken + 100 restored = 200.
1249
+ let val = dapp_service::get_user_field<ListKey>(&us, gold_key(), b"amount");
1250
+ let amount = sui::bcs::new(val).peel_u64();
1251
+ assert!(amount == 200, 1);
1252
+
1253
+ dapp_service::destroy_user_storage(us);
1254
+ dapp_service::destroy_dapp_hub(dh);
1255
+ dapp_service::destroy_dapp_storage(ds);
1256
+ }
1257
+
1258
+ // ─── update_marketplace_dapp_share edge cases ─────────────────────────────────
1259
+
1260
+ /// Non-admin must not be able to change the DApp revenue share.
1261
+ #[test]
1262
+ #[expected_failure]
1263
+ fun test_update_marketplace_dapp_share_non_admin_aborts() {
1264
+ let ctx = &mut tx_context::dummy();
1265
+ let mut dh = dapp_service::create_dapp_hub_for_testing(ctx);
1266
+ let ctx_evil = &mut tx_context::new_from_hint(@0xDEAD, 0, 0, 0, 0);
1267
+ dapp_system::update_marketplace_dapp_share(&mut dh, 7_000, ctx_evil);
1268
+ dapp_service::destroy_dapp_hub(dh);
1269
+ }
1270
+
1271
+ /// share_bps > 10_000 (> 100%) must abort.
1272
+ #[test]
1273
+ #[expected_failure]
1274
+ fun test_update_marketplace_dapp_share_over_10000_aborts() {
1275
+ let ctx = &mut tx_context::dummy();
1276
+ let mut dh = dapp_service::create_dapp_hub_for_testing(ctx);
1277
+ dapp_system::update_marketplace_dapp_share(&mut dh, 10_001, ctx);
1278
+ dapp_service::destroy_dapp_hub(dh);
1279
+ }
1280
+
1281
+ // ─── settle_marketplace_fee edge cases ────────────────────────────────────────
1282
+
1283
+ /// Passing a DappStorage that belongs to a different DApp must abort.
1284
+ #[test]
1285
+ #[expected_failure]
1286
+ fun test_settle_marketplace_fee_dapp_key_mismatch_aborts() {
1287
+ let ctx = &mut tx_context::dummy();
1288
+ let dh = dapp_service::create_dapp_hub_for_testing(ctx);
1289
+ // DappStorage created for OtherDappKey, but called with ListKey auth.
1290
+ let mut ds_other = dapp_service::create_dapp_storage_for_testing<OtherDappKey>(ctx);
1291
+ let fee_coin = sui::coin::mint_for_testing<SUI>(100, ctx);
1292
+ dapp_system::settle_marketplace_fee<ListKey, SUI>(
1293
+ ListKey {}, &dh, &mut ds_other, fee_coin, @0x0, ctx
1294
+ );
1295
+ dapp_service::destroy_dapp_hub(dh);
1296
+ dapp_service::destroy_dapp_storage(ds_other);
1297
+ }
1298
+
1299
+ /// When share_bps == 10_000 (100% to DApp), the full fee goes to dapp_storage
1300
+ /// and nothing is transferred to the framework treasury.
1301
+ #[test]
1302
+ fun test_settle_marketplace_fee_all_to_dapp() {
1303
+ let ctx = &mut tx_context::dummy();
1304
+ let mut dh = dapp_service::create_dapp_hub_for_testing(ctx);
1305
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(ctx);
1306
+ // Set DApp share to 100%.
1307
+ dapp_system::update_marketplace_dapp_share(&mut dh, 10_000, ctx);
1308
+ let fee_coin = sui::coin::mint_for_testing<SUI>(200, ctx);
1309
+ dapp_system::settle_marketplace_fee<ListKey, SUI>(
1310
+ ListKey {}, &dh, &mut ds, fee_coin, @0x0, ctx
1311
+ );
1312
+ // All 200 must be in the DApp revenue pool.
1313
+ assert!(dapp_service::dapp_revenue_balance<SUI>(&ds) == 200, 0);
1314
+ dapp_service::destroy_dapp_hub(dh);
1315
+ dapp_service::destroy_dapp_storage(ds);
1316
+ }
1317
+
1318
+ /// When share_bps == 0 (0% to DApp), the full fee goes to the framework
1319
+ /// treasury and dapp_storage revenue stays at zero.
1320
+ #[test]
1321
+ fun test_settle_marketplace_fee_all_to_framework() {
1322
+ let ctx = &mut tx_context::dummy();
1323
+ let mut dh = dapp_service::create_dapp_hub_for_testing(ctx);
1324
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(ctx);
1325
+ // Set DApp share to 0%.
1326
+ dapp_system::update_marketplace_dapp_share(&mut dh, 0, ctx);
1327
+ let fee_coin = sui::coin::mint_for_testing<SUI>(200, ctx);
1328
+ dapp_system::settle_marketplace_fee<ListKey, SUI>(
1329
+ ListKey {}, &dh, &mut ds, fee_coin, @0x0, ctx
1330
+ );
1331
+ // DApp revenue pool must remain empty.
1332
+ assert!(dapp_service::dapp_revenue_balance<SUI>(&ds) == 0, 0);
1333
+ dapp_service::destroy_dapp_hub(dh);
1334
+ dapp_service::destroy_dapp_storage(ds);
1335
+ }
1336
+
1337
+ // ─── Pause guard: buy / settle paths abort when DApp is paused ───────────────
1338
+
1339
+ /// buy_record must abort with EDappPaused when the DApp is paused.
1340
+ #[test]
1341
+ #[expected_failure]
1342
+ fun test_buy_record_aborts_when_paused() {
1343
+ let mut ctx = sui::tx_context::dummy();
1344
+ let dh = dapp_service::create_dapp_hub_for_testing(&mut ctx);
1345
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(&mut ctx);
1346
+ let mut buyer_us = make_us(ctx.sender(), &mut ctx);
1347
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&buyer_us);
1348
+
1349
+ let listing = dapp_service::new_listing<SUI>(
1350
+ weapon_values(100, 1),
1351
+ b"weapon",
1352
+ weapon_key(1),
1353
+ weapon_fields(),
1354
+ @0x1234, // seller ≠ buyer
1355
+ 50,
1356
+ std::option::none(),
1357
+ dapp_key_str,
1358
+ false,
1359
+ &mut ctx,
1360
+ );
1361
+ let payment = sui::coin::mint_for_testing<SUI>(50, &mut ctx);
1362
+ dapp_system::set_paused<ListKey>(&dh, &mut ds, true, &mut ctx);
1363
+ // Must abort here — unreachable cleanup required by Move compiler.
1364
+ let change = dapp_system::buy_record<ListKey, SUI>(
1365
+ ListKey {}, &dh, &mut ds, listing, &mut buyer_us, payment, &mut ctx
1366
+ );
1367
+ sui::coin::burn_for_testing(change);
1368
+ dapp_service::destroy_user_storage(buyer_us);
1369
+ dapp_service::destroy_dapp_hub(dh);
1370
+ dapp_service::destroy_dapp_storage(ds);
1371
+ }
1372
+
1373
+ /// buy_fungible_record must abort with EDappPaused when the DApp is paused.
1374
+ #[test]
1375
+ #[expected_failure]
1376
+ fun test_buy_fungible_record_aborts_when_paused() {
1377
+ let mut ctx = sui::tx_context::dummy();
1378
+ let dh = dapp_service::create_dapp_hub_for_testing(&mut ctx);
1379
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(&mut ctx);
1380
+ let mut buyer_us = make_us(ctx.sender(), &mut ctx);
1381
+ let dapp_key_str = dapp_service::user_storage_dapp_key(&buyer_us);
1382
+
1383
+ let listing = dapp_service::new_listing<SUI>(
1384
+ vector[to_bytes(&100u64)],
1385
+ b"gold",
1386
+ gold_key(),
1387
+ vector[b"amount"],
1388
+ @0x1234, // seller ≠ buyer
1389
+ 10,
1390
+ std::option::none(),
1391
+ dapp_key_str,
1392
+ true,
1393
+ &mut ctx,
1394
+ );
1395
+ let payment = sui::coin::mint_for_testing<SUI>(10, &mut ctx);
1396
+ dapp_system::set_paused<ListKey>(&dh, &mut ds, true, &mut ctx);
1397
+ // Must abort here — unreachable cleanup required by Move compiler.
1398
+ let change = dapp_system::buy_fungible_record<ListKey, SUI>(
1399
+ ListKey {}, &dh, &mut ds, listing, &mut buyer_us, payment, &mut ctx
1400
+ );
1401
+ sui::coin::burn_for_testing(change);
1402
+ dapp_service::destroy_user_storage(buyer_us);
1403
+ dapp_service::destroy_dapp_hub(dh);
1404
+ dapp_service::destroy_dapp_storage(ds);
1405
+ }
1406
+
1407
+ /// settle_marketplace_fee must abort with EDappPaused when the DApp is paused.
1408
+ #[test]
1409
+ #[expected_failure]
1410
+ fun test_settle_marketplace_fee_aborts_when_paused() {
1411
+ let mut ctx = sui::tx_context::dummy();
1412
+ let dh = dapp_service::create_dapp_hub_for_testing(&mut ctx);
1413
+ let mut ds = dapp_service::create_dapp_storage_for_testing<ListKey>(&mut ctx);
1414
+ dapp_system::set_paused<ListKey>(&dh, &mut ds, true, &mut ctx);
1415
+ let fee_coin = sui::coin::mint_for_testing<SUI>(100, &mut ctx);
1416
+ // Must abort here — unreachable cleanup required by Move compiler.
1417
+ dapp_system::settle_marketplace_fee<ListKey, SUI>(
1418
+ ListKey {}, &dh, &mut ds, fee_coin, @0x0, &mut ctx
1419
+ );
1420
+ dapp_service::destroy_dapp_hub(dh);
1421
+ dapp_service::destroy_dapp_storage(ds);
1422
+ }