@layerzerolabs/protocol-starknet-v2 0.0.34

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 (504) hide show
  1. package/.turbo/turbo-build.log +186 -0
  2. package/.turbo/turbo-lint.log +71 -0
  3. package/.turbo/turbo-test.log +937 -0
  4. package/README.md +41 -0
  5. package/Scarb.lock +211 -0
  6. package/Scarb.toml +2 -0
  7. package/dist/4XD3ZRZ4.cjs +301 -0
  8. package/dist/4XD3ZRZ4.cjs.map +1 -0
  9. package/dist/4Z5IPBC3.js +299 -0
  10. package/dist/4Z5IPBC3.js.map +1 -0
  11. package/dist/5NEZDLVQ.cjs +474 -0
  12. package/dist/5NEZDLVQ.cjs.map +1 -0
  13. package/dist/6JYCOKDE.js +472 -0
  14. package/dist/6JYCOKDE.js.map +1 -0
  15. package/dist/7C4PFMIZ.cjs +1288 -0
  16. package/dist/7C4PFMIZ.cjs.map +1 -0
  17. package/dist/7ZSGGZUE.js +1229 -0
  18. package/dist/7ZSGGZUE.js.map +1 -0
  19. package/dist/ARHOGUYH.cjs +2136 -0
  20. package/dist/ARHOGUYH.cjs.map +1 -0
  21. package/dist/CRCRIUFX.js +1264 -0
  22. package/dist/CRCRIUFX.js.map +1 -0
  23. package/dist/DB7CQSED.cjs +430 -0
  24. package/dist/DB7CQSED.cjs.map +1 -0
  25. package/dist/DFXLWHYP.cjs +1266 -0
  26. package/dist/DFXLWHYP.cjs.map +1 -0
  27. package/dist/EOLZCMCK.js +988 -0
  28. package/dist/EOLZCMCK.js.map +1 -0
  29. package/dist/FFDPTOWG.cjs +331 -0
  30. package/dist/FFDPTOWG.cjs.map +1 -0
  31. package/dist/FOJGEAIO.js +2134 -0
  32. package/dist/FOJGEAIO.js.map +1 -0
  33. package/dist/IWIUMVGB.js +629 -0
  34. package/dist/IWIUMVGB.js.map +1 -0
  35. package/dist/MUEN6AWV.cjs +697 -0
  36. package/dist/MUEN6AWV.cjs.map +1 -0
  37. package/dist/ORE6VBZ4.cjs +990 -0
  38. package/dist/ORE6VBZ4.cjs.map +1 -0
  39. package/dist/OUFKWPZ7.js +732 -0
  40. package/dist/OUFKWPZ7.js.map +1 -0
  41. package/dist/T2QTYQXJ.js +1229 -0
  42. package/dist/T2QTYQXJ.js.map +1 -0
  43. package/dist/UPJTM7BR.cjs +631 -0
  44. package/dist/UPJTM7BR.cjs.map +1 -0
  45. package/dist/VNVNX2P3.cjs +1231 -0
  46. package/dist/VNVNX2P3.cjs.map +1 -0
  47. package/dist/VUOMXK5T.js +6 -0
  48. package/dist/VUOMXK5T.js.map +1 -0
  49. package/dist/WISWRTDG.js +1286 -0
  50. package/dist/WISWRTDG.js.map +1 -0
  51. package/dist/WU5L7YIQ.cjs +1231 -0
  52. package/dist/WU5L7YIQ.cjs.map +1 -0
  53. package/dist/X3B5JDMZ.js +695 -0
  54. package/dist/X3B5JDMZ.js.map +1 -0
  55. package/dist/XYNBDBBV.cjs +297 -0
  56. package/dist/XYNBDBBV.cjs.map +1 -0
  57. package/dist/Y5JFPCYJ.cjs +734 -0
  58. package/dist/Y5JFPCYJ.cjs.map +1 -0
  59. package/dist/YEHL7IYO.js +295 -0
  60. package/dist/YEHL7IYO.js.map +1 -0
  61. package/dist/YJF4D23A.cjs +8 -0
  62. package/dist/YJF4D23A.cjs.map +1 -0
  63. package/dist/YTS44OEA.js +428 -0
  64. package/dist/YTS44OEA.js.map +1 -0
  65. package/dist/Z2NIUZMW.js +329 -0
  66. package/dist/Z2NIUZMW.js.map +1 -0
  67. package/dist/abis/blocked-message-lib.cjs +13 -0
  68. package/dist/abis/blocked-message-lib.cjs.map +1 -0
  69. package/dist/abis/blocked-message-lib.d.ts +338 -0
  70. package/dist/abis/blocked-message-lib.d.ts.map +1 -0
  71. package/dist/abis/blocked-message-lib.js +4 -0
  72. package/dist/abis/blocked-message-lib.js.map +1 -0
  73. package/dist/abis/dvn-fee-lib.cjs +13 -0
  74. package/dist/abis/dvn-fee-lib.cjs.map +1 -0
  75. package/dist/abis/dvn-fee-lib.d.ts +214 -0
  76. package/dist/abis/dvn-fee-lib.d.ts.map +1 -0
  77. package/dist/abis/dvn-fee-lib.js +4 -0
  78. package/dist/abis/dvn-fee-lib.js.map +1 -0
  79. package/dist/abis/dvn.cjs +13 -0
  80. package/dist/abis/dvn.cjs.map +1 -0
  81. package/dist/abis/dvn.d.ts +952 -0
  82. package/dist/abis/dvn.d.ts.map +1 -0
  83. package/dist/abis/dvn.js +4 -0
  84. package/dist/abis/dvn.js.map +1 -0
  85. package/dist/abis/endpoint-v2.cjs +13 -0
  86. package/dist/abis/endpoint-v2.cjs.map +1 -0
  87. package/dist/abis/endpoint-v2.d.ts +1580 -0
  88. package/dist/abis/endpoint-v2.d.ts.map +1 -0
  89. package/dist/abis/endpoint-v2.js +4 -0
  90. package/dist/abis/endpoint-v2.js.map +1 -0
  91. package/dist/abis/executor-fee-lib.cjs +13 -0
  92. package/dist/abis/executor-fee-lib.cjs.map +1 -0
  93. package/dist/abis/executor-fee-lib.d.ts +217 -0
  94. package/dist/abis/executor-fee-lib.d.ts.map +1 -0
  95. package/dist/abis/executor-fee-lib.js +4 -0
  96. package/dist/abis/executor-fee-lib.js.map +1 -0
  97. package/dist/abis/executor.cjs +13 -0
  98. package/dist/abis/executor.cjs.map +1 -0
  99. package/dist/abis/executor.d.ts +914 -0
  100. package/dist/abis/executor.d.ts.map +1 -0
  101. package/dist/abis/executor.js +4 -0
  102. package/dist/abis/executor.js.map +1 -0
  103. package/dist/abis/o-app.cjs +13 -0
  104. package/dist/abis/o-app.cjs.map +1 -0
  105. package/dist/abis/o-app.d.ts +311 -0
  106. package/dist/abis/o-app.d.ts.map +1 -0
  107. package/dist/abis/o-app.js +4 -0
  108. package/dist/abis/o-app.js.map +1 -0
  109. package/dist/abis/oft-adapter.cjs +13 -0
  110. package/dist/abis/oft-adapter.cjs.map +1 -0
  111. package/dist/abis/oft-adapter.d.ts +722 -0
  112. package/dist/abis/oft-adapter.d.ts.map +1 -0
  113. package/dist/abis/oft-adapter.js +4 -0
  114. package/dist/abis/oft-adapter.js.map +1 -0
  115. package/dist/abis/oft.cjs +13 -0
  116. package/dist/abis/oft.cjs.map +1 -0
  117. package/dist/abis/oft.d.ts +922 -0
  118. package/dist/abis/oft.d.ts.map +1 -0
  119. package/dist/abis/oft.js +4 -0
  120. package/dist/abis/oft.js.map +1 -0
  121. package/dist/abis/omni-counter.cjs +13 -0
  122. package/dist/abis/omni-counter.cjs.map +1 -0
  123. package/dist/abis/omni-counter.d.ts +459 -0
  124. package/dist/abis/omni-counter.d.ts.map +1 -0
  125. package/dist/abis/omni-counter.js +4 -0
  126. package/dist/abis/omni-counter.js.map +1 -0
  127. package/dist/abis/price-feed.cjs +13 -0
  128. package/dist/abis/price-feed.cjs.map +1 -0
  129. package/dist/abis/price-feed.d.ts +505 -0
  130. package/dist/abis/price-feed.d.ts.map +1 -0
  131. package/dist/abis/price-feed.js +4 -0
  132. package/dist/abis/price-feed.js.map +1 -0
  133. package/dist/abis/simple-message-lib.cjs +13 -0
  134. package/dist/abis/simple-message-lib.cjs.map +1 -0
  135. package/dist/abis/simple-message-lib.d.ts +535 -0
  136. package/dist/abis/simple-message-lib.d.ts.map +1 -0
  137. package/dist/abis/simple-message-lib.js +4 -0
  138. package/dist/abis/simple-message-lib.js.map +1 -0
  139. package/dist/abis/treasury.cjs +13 -0
  140. package/dist/abis/treasury.cjs.map +1 -0
  141. package/dist/abis/treasury.d.ts +240 -0
  142. package/dist/abis/treasury.d.ts.map +1 -0
  143. package/dist/abis/treasury.js +4 -0
  144. package/dist/abis/treasury.js.map +1 -0
  145. package/dist/abis/ultra-light-node.cjs +13 -0
  146. package/dist/abis/ultra-light-node.cjs.map +1 -0
  147. package/dist/abis/ultra-light-node.d.ts +900 -0
  148. package/dist/abis/ultra-light-node.d.ts.map +1 -0
  149. package/dist/abis/ultra-light-node.js +4 -0
  150. package/dist/abis/ultra-light-node.js.map +1 -0
  151. package/dist/index.cjs +78 -0
  152. package/dist/index.cjs.map +1 -0
  153. package/dist/index.d.ts +15 -0
  154. package/dist/index.d.ts.map +1 -0
  155. package/dist/index.js +17 -0
  156. package/dist/index.js.map +1 -0
  157. package/dist/scripts/build-abi.cjs +28 -0
  158. package/dist/scripts/build-abi.cjs.map +1 -0
  159. package/dist/scripts/build-abi.d.ts +2 -0
  160. package/dist/scripts/build-abi.d.ts.map +1 -0
  161. package/dist/scripts/build-abi.js +26 -0
  162. package/dist/scripts/build-abi.js.map +1 -0
  163. package/layerzero/README.md +244 -0
  164. package/layerzero/Scarb.lock +203 -0
  165. package/layerzero/Scarb.toml +30 -0
  166. package/layerzero/snfoundry.toml +11 -0
  167. package/layerzero/src/common/constants.cairo +26 -0
  168. package/layerzero/src/common/conversions.cairo +16 -0
  169. package/layerzero/src/common/guid.cairo +20 -0
  170. package/layerzero/src/common/packet_v1_codec.cairo +307 -0
  171. package/layerzero/src/common/structs/messaging.cairo +40 -0
  172. package/layerzero/src/common/structs/packet.cairo +31 -0
  173. package/layerzero/src/endpoint/constants.cairo +14 -0
  174. package/layerzero/src/endpoint/endpoint.cairo +688 -0
  175. package/layerzero/src/endpoint/errors.cairo +108 -0
  176. package/layerzero/src/endpoint/events.cairo +124 -0
  177. package/layerzero/src/endpoint/interfaces/endpoint.cairo +286 -0
  178. package/layerzero/src/endpoint/interfaces/layerzero_composer.cairo +62 -0
  179. package/layerzero/src/endpoint/interfaces/layerzero_receiver.cairo +63 -0
  180. package/layerzero/src/endpoint/message_lib_manager/errors.cairo +95 -0
  181. package/layerzero/src/endpoint/message_lib_manager/events.cairo +90 -0
  182. package/layerzero/src/endpoint/message_lib_manager/interface.cairo +449 -0
  183. package/layerzero/src/endpoint/message_lib_manager/message_lib_manager.cairo +720 -0
  184. package/layerzero/src/endpoint/message_lib_manager/structs.cairo +33 -0
  185. package/layerzero/src/endpoint/messaging_channel/errors.cairo +37 -0
  186. package/layerzero/src/endpoint/messaging_channel/events.cairo +58 -0
  187. package/layerzero/src/endpoint/messaging_channel/interface.cairo +171 -0
  188. package/layerzero/src/endpoint/messaging_channel/messaging_channel.cairo +453 -0
  189. package/layerzero/src/endpoint/messaging_composer/errors.cairo +46 -0
  190. package/layerzero/src/endpoint/messaging_composer/events.cairo +67 -0
  191. package/layerzero/src/endpoint/messaging_composer/interface.cairo +132 -0
  192. package/layerzero/src/endpoint/messaging_composer/messaging_composer.cairo +223 -0
  193. package/layerzero/src/lib.cairo +189 -0
  194. package/layerzero/src/message_lib/blocked_message_lib.cairo +114 -0
  195. package/layerzero/src/message_lib/interface.cairo +63 -0
  196. package/layerzero/src/message_lib/sml/errors.cairo +23 -0
  197. package/layerzero/src/message_lib/sml/events.cairo +32 -0
  198. package/layerzero/src/message_lib/sml/simple_message_lib.cairo +312 -0
  199. package/layerzero/src/message_lib/structs.cairo +22 -0
  200. package/layerzero/src/message_lib/uln/errors.cairo +128 -0
  201. package/layerzero/src/message_lib/uln/events.cairo +97 -0
  202. package/layerzero/src/message_lib/uln/interface.cairo +83 -0
  203. package/layerzero/src/message_lib/uln/options.cairo +64 -0
  204. package/layerzero/src/message_lib/uln/structs/executor_config.cairo +35 -0
  205. package/layerzero/src/message_lib/uln/structs/payment_info.cairo +7 -0
  206. package/layerzero/src/message_lib/uln/structs/uln_config.cairo +155 -0
  207. package/layerzero/src/message_lib/uln/structs/uln_config_storage_node.cairo +91 -0
  208. package/layerzero/src/message_lib/uln/structs/verification.cairo +7 -0
  209. package/layerzero/src/message_lib/uln/ultra_light_node.cairo +965 -0
  210. package/layerzero/src/oapps/common/oapp_options_type_3/errors.cairo +22 -0
  211. package/layerzero/src/oapps/common/oapp_options_type_3/events.cairo +6 -0
  212. package/layerzero/src/oapps/common/oapp_options_type_3/interface.cairo +34 -0
  213. package/layerzero/src/oapps/common/oapp_options_type_3/oapp_options_type_3.cairo +120 -0
  214. package/layerzero/src/oapps/common/oapp_options_type_3/structs.cairo +6 -0
  215. package/layerzero/src/oapps/counter/constants.cairo +3 -0
  216. package/layerzero/src/oapps/counter/counter.cairo +170 -0
  217. package/layerzero/src/oapps/counter/interface.cairo +27 -0
  218. package/layerzero/src/oapps/counter/structs.cairo +20 -0
  219. package/layerzero/src/oapps/message_inspector/interface.cairo +21 -0
  220. package/layerzero/src/oapps/oapp/errors.cairo +72 -0
  221. package/layerzero/src/oapps/oapp/events.cairo +9 -0
  222. package/layerzero/src/oapps/oapp/interface.cairo +67 -0
  223. package/layerzero/src/oapps/oapp/oapp.cairo +70 -0
  224. package/layerzero/src/oapps/oapp/oapp_core.cairo +448 -0
  225. package/layerzero/src/oapps/oft/errors.cairo +42 -0
  226. package/layerzero/src/oapps/oft/events.cairo +33 -0
  227. package/layerzero/src/oapps/oft/interface.cairo +87 -0
  228. package/layerzero/src/oapps/oft/oft.cairo +188 -0
  229. package/layerzero/src/oapps/oft/oft_adapter.cairo +175 -0
  230. package/layerzero/src/oapps/oft/oft_compose_msg_codec.cairo +128 -0
  231. package/layerzero/src/oapps/oft/oft_core.cairo +542 -0
  232. package/layerzero/src/oapps/oft/oft_msg_codec.cairo +131 -0
  233. package/layerzero/src/oapps/oft/structs.cairo +72 -0
  234. package/layerzero/src/treasury/errors.cairo +22 -0
  235. package/layerzero/src/treasury/events.cairo +5 -0
  236. package/layerzero/src/treasury/interfaces/layerzero_treasury.cairo +54 -0
  237. package/layerzero/src/treasury/interfaces/lz_token_fee_lib.cairo +45 -0
  238. package/layerzero/src/treasury/interfaces/treasury_admin.cairo +39 -0
  239. package/layerzero/src/treasury/treasury.cairo +140 -0
  240. package/layerzero/src/workers/access_control.cairo +11 -0
  241. package/layerzero/src/workers/base/base.cairo +238 -0
  242. package/layerzero/src/workers/base/errors.cairo +24 -0
  243. package/layerzero/src/workers/base/events.cairo +43 -0
  244. package/layerzero/src/workers/base/interface.cairo +93 -0
  245. package/layerzero/src/workers/base/structs.cairo +10 -0
  246. package/layerzero/src/workers/common.cairo +59 -0
  247. package/layerzero/src/workers/dvn/constants.cairo +11 -0
  248. package/layerzero/src/workers/dvn/dvn.cairo +338 -0
  249. package/layerzero/src/workers/dvn/errors.cairo +80 -0
  250. package/layerzero/src/workers/dvn/events.cairo +30 -0
  251. package/layerzero/src/workers/dvn/fee_lib/dvn_fee_lib.cairo +152 -0
  252. package/layerzero/src/workers/dvn/fee_lib/interface.cairo +45 -0
  253. package/layerzero/src/workers/dvn/interface.cairo +131 -0
  254. package/layerzero/src/workers/dvn/options.cairo +125 -0
  255. package/layerzero/src/workers/dvn/structs.cairo +51 -0
  256. package/layerzero/src/workers/executor/errors.cairo +159 -0
  257. package/layerzero/src/workers/executor/events.cairo +32 -0
  258. package/layerzero/src/workers/executor/executor.cairo +392 -0
  259. package/layerzero/src/workers/executor/fee_lib/executor_fee_lib.cairo +160 -0
  260. package/layerzero/src/workers/executor/fee_lib/interface.cairo +87 -0
  261. package/layerzero/src/workers/executor/interface.cairo +131 -0
  262. package/layerzero/src/workers/executor/options.cairo +244 -0
  263. package/layerzero/src/workers/executor/structs.cairo +119 -0
  264. package/layerzero/src/workers/interface.cairo +32 -0
  265. package/layerzero/src/workers/price_feed/constants.cairo +7 -0
  266. package/layerzero/src/workers/price_feed/errors.cairo +28 -0
  267. package/layerzero/src/workers/price_feed/events.cairo +13 -0
  268. package/layerzero/src/workers/price_feed/interface.cairo +264 -0
  269. package/layerzero/src/workers/price_feed/price_feed.cairo +392 -0
  270. package/layerzero/src/workers/price_feed/structs.cairo +74 -0
  271. package/layerzero/tests/common/test_constants.cairo +21 -0
  272. package/layerzero/tests/common/test_guid.cairo +232 -0
  273. package/layerzero/tests/common/test_packet_v1_codec.cairo +372 -0
  274. package/layerzero/tests/common/utils.cairo +23 -0
  275. package/layerzero/tests/e2e/oft_utils.cairo +121 -0
  276. package/layerzero/tests/e2e/test_counter_with_sml.cairo +194 -0
  277. package/layerzero/tests/e2e/test_counter_with_uln.cairo +352 -0
  278. package/layerzero/tests/e2e/test_dvn.cairo +406 -0
  279. package/layerzero/tests/e2e/test_lz_token.cairo +354 -0
  280. package/layerzero/tests/e2e/test_oft_compose_with_uln.cairo +364 -0
  281. package/layerzero/tests/e2e/test_oft_with_sml.cairo +240 -0
  282. package/layerzero/tests/e2e/test_oft_with_uln.cairo +299 -0
  283. package/layerzero/tests/e2e/utils.cairo +490 -0
  284. package/layerzero/tests/endpoint/message_lib_manager/test_message_lib_manager.cairo +2051 -0
  285. package/layerzero/tests/endpoint/message_lib_manager/utils.cairo +45 -0
  286. package/layerzero/tests/endpoint/messaging_channel/test_messaging_channel.cairo +621 -0
  287. package/layerzero/tests/endpoint/messaging_channel/utils.cairo +96 -0
  288. package/layerzero/tests/endpoint/messaging_composer/test_messaging_composer.cairo +456 -0
  289. package/layerzero/tests/endpoint/messaging_composer/utils.cairo +75 -0
  290. package/layerzero/tests/endpoint/test_endpoint_commit.cairo +763 -0
  291. package/layerzero/tests/endpoint/test_endpoint_lzreceive.cairo +1253 -0
  292. package/layerzero/tests/endpoint/test_endpoint_quote.cairo +71 -0
  293. package/layerzero/tests/endpoint/test_endpoint_send.cairo +1327 -0
  294. package/layerzero/tests/endpoint/utils.cairo +129 -0
  295. package/layerzero/tests/fuzzable/blockchain_config.cairo +89 -0
  296. package/layerzero/tests/fuzzable/bytes32.cairo +16 -0
  297. package/layerzero/tests/fuzzable/contract_address.cairo +67 -0
  298. package/layerzero/tests/fuzzable/dst_config.cairo +37 -0
  299. package/layerzero/tests/fuzzable/eid.cairo +23 -0
  300. package/layerzero/tests/fuzzable/eth_address.cairo +17 -0
  301. package/layerzero/tests/fuzzable/expiry.cairo +27 -0
  302. package/layerzero/tests/fuzzable/felt_array.cairo +38 -0
  303. package/layerzero/tests/fuzzable/inbound_params.cairo +21 -0
  304. package/layerzero/tests/fuzzable/keys.cairo +16 -0
  305. package/layerzero/tests/fuzzable/model_type.cairo +27 -0
  306. package/layerzero/tests/fuzzable/origin.cairo +21 -0
  307. package/layerzero/tests/fuzzable/price.cairo +32 -0
  308. package/layerzero/tests/fuzzable/role_admin.cairo +29 -0
  309. package/layerzero/tests/fuzzable/small_byte_array.cairo +61 -0
  310. package/layerzero/tests/lib.cairo +177 -0
  311. package/layerzero/tests/message_lib/sml/test_simple_message_lib.cairo +224 -0
  312. package/layerzero/tests/message_lib/uln/test_uln_admin.cairo +2150 -0
  313. package/layerzero/tests/message_lib/uln/test_uln_config.cairo +527 -0
  314. package/layerzero/tests/message_lib/uln/test_uln_config_storage_node.cairo +69 -0
  315. package/layerzero/tests/message_lib/uln/test_uln_executor_config.cairo +173 -0
  316. package/layerzero/tests/message_lib/uln/test_uln_options.cairo +329 -0
  317. package/layerzero/tests/message_lib/uln/test_uln_quote.cairo +1038 -0
  318. package/layerzero/tests/message_lib/uln/test_uln_receive.cairo +715 -0
  319. package/layerzero/tests/message_lib/uln/test_uln_send.cairo +1155 -0
  320. package/layerzero/tests/message_lib/uln/utils.cairo +59 -0
  321. package/layerzero/tests/mocks/composer_target.cairo +76 -0
  322. package/layerzero/tests/mocks/endpoint.cairo +199 -0
  323. package/layerzero/tests/mocks/erc20/erc20.cairo +50 -0
  324. package/layerzero/tests/mocks/erc20/interface.cairo +8 -0
  325. package/layerzero/tests/mocks/message_inspector/message_inspector.cairo +17 -0
  326. package/layerzero/tests/mocks/message_lib_manager.cairo +98 -0
  327. package/layerzero/tests/mocks/messaging_channel/interface.cairo +23 -0
  328. package/layerzero/tests/mocks/messaging_channel/messaging_channel.cairo +138 -0
  329. package/layerzero/tests/mocks/messaging_composer.cairo +171 -0
  330. package/layerzero/tests/mocks/oapp_core/interface.cairo +53 -0
  331. package/layerzero/tests/mocks/oapp_core/oapp_core.cairo +142 -0
  332. package/layerzero/tests/mocks/oapp_options_type3.cairo +42 -0
  333. package/layerzero/tests/mocks/oft_core/interface.cairo +28 -0
  334. package/layerzero/tests/mocks/oft_core/oft_core.cairo +242 -0
  335. package/layerzero/tests/mocks/receiver.cairo +54 -0
  336. package/layerzero/tests/mocks/treasury/lz_token_fee_lib.cairo +57 -0
  337. package/layerzero/tests/mocks/treasury/treasury.cairo +74 -0
  338. package/layerzero/tests/mocks/uln_config/interface.cairo +12 -0
  339. package/layerzero/tests/mocks/uln_config/uln_config.cairo +35 -0
  340. package/layerzero/tests/mocks/workers/base.cairo +80 -0
  341. package/layerzero/tests/mocks/workers/dvn.cairo +115 -0
  342. package/layerzero/tests/mocks/workers/executor/decode/decode.cairo +97 -0
  343. package/layerzero/tests/mocks/workers/executor/decode/interface.cairo +59 -0
  344. package/layerzero/tests/mocks/workers/executor/executor.cairo +176 -0
  345. package/layerzero/tests/oapps/common/test_oapp_options_type_3.cairo +279 -0
  346. package/layerzero/tests/oapps/oft/test_oft_adapter.cairo +441 -0
  347. package/layerzero/tests/oapps/oft/test_oft_compose_msg_codec.cairo +139 -0
  348. package/layerzero/tests/oapps/oft/test_oft_core.cairo +751 -0
  349. package/layerzero/tests/oapps/oft/test_oft_msg_codec.cairo +268 -0
  350. package/layerzero/tests/oapps/test_counter.cairo +470 -0
  351. package/layerzero/tests/oapps/test_oapp_core.cairo +750 -0
  352. package/layerzero/tests/treasury/test_lz_token_fee_lib.cairo +63 -0
  353. package/layerzero/tests/treasury/test_treasury.cairo +458 -0
  354. package/layerzero/tests/treasury/utils.cairo +8 -0
  355. package/layerzero/tests/utils.cairo +48 -0
  356. package/layerzero/tests/workers/base/test_worker_base.cairo +1097 -0
  357. package/layerzero/tests/workers/base/utils.cairo +76 -0
  358. package/layerzero/tests/workers/dvn/fee_lib/test_dvn_fee_lib.cairo +361 -0
  359. package/layerzero/tests/workers/dvn/test_dvn.cairo +1101 -0
  360. package/layerzero/tests/workers/dvn/test_dvn_options.cairo +312 -0
  361. package/layerzero/tests/workers/dvn/utils.cairo +236 -0
  362. package/layerzero/tests/workers/executor/fee_lib/test_executor_fee_lib.cairo +223 -0
  363. package/layerzero/tests/workers/executor/test_decode.cairo +612 -0
  364. package/layerzero/tests/workers/executor/test_executor.cairo +1472 -0
  365. package/layerzero/tests/workers/executor/utils.cairo +296 -0
  366. package/layerzero/tests/workers/price_feed/test_price_feed.cairo +879 -0
  367. package/layerzero/tests/workers/price_feed/utils.cairo +37 -0
  368. package/libs/enumerable_set/Scarb.lock +24 -0
  369. package/libs/enumerable_set/Scarb.toml +17 -0
  370. package/libs/enumerable_set/src/enumerable_set.cairo +118 -0
  371. package/libs/enumerable_set/src/lib.cairo +4 -0
  372. package/libs/enumerable_set/tests/lib.cairo +5 -0
  373. package/libs/enumerable_set/tests/mocks/mock_enumerable_set.cairo +61 -0
  374. package/libs/enumerable_set/tests/test_enumerable_set.cairo +379 -0
  375. package/libs/lz_utils/Scarb.lock +24 -0
  376. package/libs/lz_utils/Scarb.toml +17 -0
  377. package/libs/lz_utils/src/bytes.cairo +33 -0
  378. package/libs/lz_utils/src/error.cairo +12 -0
  379. package/libs/lz_utils/src/keccak.cairo +28 -0
  380. package/libs/lz_utils/src/lib.cairo +3 -0
  381. package/libs/multisig/Scarb.lock +172 -0
  382. package/libs/multisig/Scarb.toml +23 -0
  383. package/libs/multisig/src/errors.cairo +84 -0
  384. package/libs/multisig/src/events.cairo +13 -0
  385. package/libs/multisig/src/interface.cairo +73 -0
  386. package/libs/multisig/src/lib.cairo +7 -0
  387. package/libs/multisig/src/multisig.cairo +241 -0
  388. package/libs/multisig/tests/lib.cairo +4 -0
  389. package/libs/multisig/tests/mocks/mock_multisig.cairo +57 -0
  390. package/libs/multisig/tests/test_multisig.cairo +452 -0
  391. package/package.json +41 -0
  392. package/src/scripts/build-abi.ts +51 -0
  393. package/target/CACHEDIR.TAG +3 -0
  394. package/target/dev/.fingerprint/alexandria_bytes-5ea6u5t70d7qi/alexandria_bytes +1 -0
  395. package/target/dev/.fingerprint/alexandria_data_structures-0aue3g6q80gs0/alexandria_data_structures +1 -0
  396. package/target/dev/.fingerprint/alexandria_math-h2fi7jdq4isuu/alexandria_math +1 -0
  397. package/target/dev/.fingerprint/core-lq3u730l5p1ag/core +1 -0
  398. package/target/dev/.fingerprint/core-vf7fc6rvic5vi/core +1 -0
  399. package/target/dev/.fingerprint/enumerable_set-eaerkg8njl85o/enumerable_set +1 -0
  400. package/target/dev/.fingerprint/enumerable_set-r54oje7t06ku8/enumerable_set +1 -0
  401. package/target/dev/.fingerprint/layerzero-oqgdqsaddpi2k/layerzero +1 -0
  402. package/target/dev/.fingerprint/lz_utils-kfkkeueiqg0pa/lz_utils +1 -0
  403. package/target/dev/.fingerprint/lz_utils-u4v1os6e7gkng/lz_utils +1 -0
  404. package/target/dev/.fingerprint/multisig-0fjetugejecge/multisig +1 -0
  405. package/target/dev/.fingerprint/multisig-6j5kqs436hm54/multisig +1 -0
  406. package/target/dev/.fingerprint/openzeppelin-ei1id1hu088lo/openzeppelin +1 -0
  407. package/target/dev/.fingerprint/openzeppelin-j9d5nd1qhfnu6/openzeppelin +1 -0
  408. package/target/dev/.fingerprint/openzeppelin_access-3oa41aikpaek0/openzeppelin_access +1 -0
  409. package/target/dev/.fingerprint/openzeppelin_access-p5h849v8so76q/openzeppelin_access +1 -0
  410. package/target/dev/.fingerprint/openzeppelin_account-4qhv5fks84g9u/openzeppelin_account +1 -0
  411. package/target/dev/.fingerprint/openzeppelin_account-hgbm8ln9ah7rm/openzeppelin_account +1 -0
  412. package/target/dev/.fingerprint/openzeppelin_finance-n70q9al0cps8i/openzeppelin_finance +1 -0
  413. package/target/dev/.fingerprint/openzeppelin_finance-nnd4f8703t3ak/openzeppelin_finance +1 -0
  414. package/target/dev/.fingerprint/openzeppelin_governance-3gnk21ubp5lis/openzeppelin_governance +1 -0
  415. package/target/dev/.fingerprint/openzeppelin_governance-rj1bfont4fij4/openzeppelin_governance +1 -0
  416. package/target/dev/.fingerprint/openzeppelin_introspection-3fja9hd1gvbcq/openzeppelin_introspection +1 -0
  417. package/target/dev/.fingerprint/openzeppelin_introspection-jc3nf5525eet6/openzeppelin_introspection +1 -0
  418. package/target/dev/.fingerprint/openzeppelin_merkle_tree-4en77ogr2r2l2/openzeppelin_merkle_tree +1 -0
  419. package/target/dev/.fingerprint/openzeppelin_merkle_tree-4t190frqs4db8/openzeppelin_merkle_tree +1 -0
  420. package/target/dev/.fingerprint/openzeppelin_presets-aqb0f6p9c0bp6/openzeppelin_presets +1 -0
  421. package/target/dev/.fingerprint/openzeppelin_presets-nseg8korhin8e/openzeppelin_presets +1 -0
  422. package/target/dev/.fingerprint/openzeppelin_security-g7p73ji1ih1qg/openzeppelin_security +1 -0
  423. package/target/dev/.fingerprint/openzeppelin_security-qp5328v80452u/openzeppelin_security +1 -0
  424. package/target/dev/.fingerprint/openzeppelin_token-jjf7tl9rphc6k/openzeppelin_token +1 -0
  425. package/target/dev/.fingerprint/openzeppelin_token-r6s43vlpj6rqk/openzeppelin_token +1 -0
  426. package/target/dev/.fingerprint/openzeppelin_upgrades-0dpbnre7engca/openzeppelin_upgrades +1 -0
  427. package/target/dev/.fingerprint/openzeppelin_upgrades-2sqgvbuv9s800/openzeppelin_upgrades +1 -0
  428. package/target/dev/.fingerprint/openzeppelin_utils-mj395ivff1ffo/openzeppelin_utils +1 -0
  429. package/target/dev/.fingerprint/openzeppelin_utils-oh1hse8sjumgm/openzeppelin_utils +1 -0
  430. package/target/dev/.fingerprint/starkware_utils-1qnnjnq0pf9u0/starkware_utils +1 -0
  431. package/target/dev/.fingerprint/starkware_utils-mh8e3te65lju4/starkware_utils +1 -0
  432. package/target/dev/enumerable_set.sierra.json +1 -0
  433. package/target/dev/incremental/alexandria_bytes-5ea6u5t70d7qi.bin +0 -0
  434. package/target/dev/incremental/alexandria_data_structures-0aue3g6q80gs0.bin +0 -0
  435. package/target/dev/incremental/alexandria_math-h2fi7jdq4isuu.bin +0 -0
  436. package/target/dev/incremental/core-lq3u730l5p1ag.bin +0 -0
  437. package/target/dev/incremental/core-vf7fc6rvic5vi.bin +0 -0
  438. package/target/dev/incremental/enumerable_set-eaerkg8njl85o.bin +0 -0
  439. package/target/dev/incremental/enumerable_set-r54oje7t06ku8.bin +0 -0
  440. package/target/dev/incremental/layerzero-oqgdqsaddpi2k.bin +0 -0
  441. package/target/dev/incremental/lz_utils-kfkkeueiqg0pa.bin +0 -0
  442. package/target/dev/incremental/lz_utils-u4v1os6e7gkng.bin +0 -0
  443. package/target/dev/incremental/multisig-0fjetugejecge.bin +0 -0
  444. package/target/dev/incremental/multisig-6j5kqs436hm54.bin +0 -0
  445. package/target/dev/incremental/openzeppelin-ei1id1hu088lo.bin +0 -0
  446. package/target/dev/incremental/openzeppelin-j9d5nd1qhfnu6.bin +0 -0
  447. package/target/dev/incremental/openzeppelin_access-3oa41aikpaek0.bin +0 -0
  448. package/target/dev/incremental/openzeppelin_access-p5h849v8so76q.bin +0 -0
  449. package/target/dev/incremental/openzeppelin_account-4qhv5fks84g9u.bin +0 -0
  450. package/target/dev/incremental/openzeppelin_account-hgbm8ln9ah7rm.bin +0 -0
  451. package/target/dev/incremental/openzeppelin_finance-n70q9al0cps8i.bin +0 -0
  452. package/target/dev/incremental/openzeppelin_finance-nnd4f8703t3ak.bin +0 -0
  453. package/target/dev/incremental/openzeppelin_governance-3gnk21ubp5lis.bin +0 -0
  454. package/target/dev/incremental/openzeppelin_governance-rj1bfont4fij4.bin +0 -0
  455. package/target/dev/incremental/openzeppelin_introspection-3fja9hd1gvbcq.bin +0 -0
  456. package/target/dev/incremental/openzeppelin_introspection-jc3nf5525eet6.bin +0 -0
  457. package/target/dev/incremental/openzeppelin_merkle_tree-4en77ogr2r2l2.bin +0 -0
  458. package/target/dev/incremental/openzeppelin_merkle_tree-4t190frqs4db8.bin +0 -0
  459. package/target/dev/incremental/openzeppelin_presets-aqb0f6p9c0bp6.bin +0 -0
  460. package/target/dev/incremental/openzeppelin_presets-nseg8korhin8e.bin +0 -0
  461. package/target/dev/incremental/openzeppelin_security-g7p73ji1ih1qg.bin +0 -0
  462. package/target/dev/incremental/openzeppelin_security-qp5328v80452u.bin +0 -0
  463. package/target/dev/incremental/openzeppelin_token-jjf7tl9rphc6k.bin +0 -0
  464. package/target/dev/incremental/openzeppelin_token-r6s43vlpj6rqk.bin +0 -0
  465. package/target/dev/incremental/openzeppelin_upgrades-0dpbnre7engca.bin +0 -0
  466. package/target/dev/incremental/openzeppelin_upgrades-2sqgvbuv9s800.bin +0 -0
  467. package/target/dev/incremental/openzeppelin_utils-mj395ivff1ffo.bin +0 -0
  468. package/target/dev/incremental/openzeppelin_utils-oh1hse8sjumgm.bin +0 -0
  469. package/target/dev/incremental/starkware_utils-1qnnjnq0pf9u0.bin +0 -0
  470. package/target/dev/incremental/starkware_utils-mh8e3te65lju4.bin +0 -0
  471. package/target/dev/layerzero.starknet_artifacts.json +1 -0
  472. package/target/dev/layerzero_BlockedMessageLib.compiled_contract_class.json +1 -0
  473. package/target/dev/layerzero_BlockedMessageLib.contract_class.json +1 -0
  474. package/target/dev/layerzero_Dvn.compiled_contract_class.json +1 -0
  475. package/target/dev/layerzero_Dvn.contract_class.json +1 -0
  476. package/target/dev/layerzero_DvnFeeLib.compiled_contract_class.json +1 -0
  477. package/target/dev/layerzero_DvnFeeLib.contract_class.json +1 -0
  478. package/target/dev/layerzero_Endpoint.compiled_contract_class.json +1 -0
  479. package/target/dev/layerzero_Endpoint.contract_class.json +1 -0
  480. package/target/dev/layerzero_Executor.compiled_contract_class.json +1 -0
  481. package/target/dev/layerzero_Executor.contract_class.json +1 -0
  482. package/target/dev/layerzero_ExecutorFeeLib.compiled_contract_class.json +1 -0
  483. package/target/dev/layerzero_ExecutorFeeLib.contract_class.json +1 -0
  484. package/target/dev/layerzero_OApp.compiled_contract_class.json +1 -0
  485. package/target/dev/layerzero_OApp.contract_class.json +1 -0
  486. package/target/dev/layerzero_OFT.compiled_contract_class.json +1 -0
  487. package/target/dev/layerzero_OFT.contract_class.json +1 -0
  488. package/target/dev/layerzero_OFTAdapter.compiled_contract_class.json +1 -0
  489. package/target/dev/layerzero_OFTAdapter.contract_class.json +1 -0
  490. package/target/dev/layerzero_OmniCounter.compiled_contract_class.json +1 -0
  491. package/target/dev/layerzero_OmniCounter.contract_class.json +1 -0
  492. package/target/dev/layerzero_PriceFeed.compiled_contract_class.json +1 -0
  493. package/target/dev/layerzero_PriceFeed.contract_class.json +1 -0
  494. package/target/dev/layerzero_SimpleMessageLib.compiled_contract_class.json +1 -0
  495. package/target/dev/layerzero_SimpleMessageLib.contract_class.json +1 -0
  496. package/target/dev/layerzero_Treasury.compiled_contract_class.json +1 -0
  497. package/target/dev/layerzero_Treasury.contract_class.json +1 -0
  498. package/target/dev/layerzero_UltraLightNode.compiled_contract_class.json +1 -0
  499. package/target/dev/layerzero_UltraLightNode.contract_class.json +1 -0
  500. package/target/dev/lz_utils.sierra.json +1 -0
  501. package/target/dev/multisig.sierra.json +1 -0
  502. package/tools/update_contracts.sh +19 -0
  503. package/tsconfig.json +20 -0
  504. package/tsup.config.ts +7 -0
@@ -0,0 +1,1097 @@
1
+ //! Base worker tests
2
+
3
+ use layerzero::common::constants::ZERO_ADDRESS;
4
+ use layerzero::workers::access_control::{ADMIN_ROLE, DEFAULT_ADMIN_ROLE, MESSAGE_LIB_ROLE};
5
+ use layerzero::workers::base::base::WorkerBaseComponent;
6
+ use layerzero::workers::base::errors;
7
+ use layerzero::workers::base::events::{
8
+ DefaultMultiplierBpsSet, FeeWithdrawn, SupportedOptionTypeSet, WorkerFeeLibSet,
9
+ };
10
+ use layerzero::workers::base::interface::{
11
+ IWorkerBaseDispatcherTrait, IWorkerBaseSafeDispatcherTrait,
12
+ };
13
+ use openzeppelin::access::accesscontrol::AccessControlComponent;
14
+ use openzeppelin::access::accesscontrol::interface::{
15
+ IAccessControlDispatcherTrait, IAccessControlSafeDispatcherTrait,
16
+ };
17
+ use openzeppelin::security::interface::{IPausableDispatcher, IPausableDispatcherTrait};
18
+ use openzeppelin::security::pausable::PausableComponent;
19
+ use openzeppelin::token::erc20::ERC20Component::{
20
+ Errors as ERC20Errors, Event as ERC20Event, Transfer,
21
+ };
22
+ use openzeppelin::token::erc20::interface::IERC20DispatcherTrait;
23
+ use snforge_std::fuzzable::{FuzzableU16, FuzzableU256};
24
+ use snforge_std::{
25
+ EventSpyAssertionsTrait, spy_events, start_cheat_caller_address, stop_cheat_caller_address,
26
+ };
27
+ use starknet::ContractAddress;
28
+ use starkware_utils_testing::test_utils::{assert_panic_with_error, assert_panic_with_felt_error};
29
+ use crate::fuzzable::contract_address::{
30
+ ContractAddressArrayList, FuzzableContractAddress, FuzzableContractAddresses,
31
+ };
32
+ use crate::fuzzable::eid::{Eid, FuzzableEid};
33
+ use crate::fuzzable::role_admin::{FuzzableRoleAdmin, RoleAdmin};
34
+ use crate::workers::base::utils::{
35
+ ERC20Mock, WorkerBaseMock, deploy_mock_erc20, deploy_worker_base,
36
+ deploy_worker_base_with_additional_roles,
37
+ };
38
+
39
+ //////////////////////////
40
+ // Initialization tests //
41
+ //////////////////////////
42
+
43
+ #[test]
44
+ #[fuzzer(runs: 10)]
45
+ fn sets_initial_roles(
46
+ role_admin: RoleAdmin,
47
+ price_feed: ContractAddress,
48
+ message_libs: ContractAddressArrayList,
49
+ admins: ContractAddressArrayList,
50
+ ) {
51
+ let role_admin = role_admin.address;
52
+ let WorkerBaseMock {
53
+ access_control, ..,
54
+ } =
55
+ deploy_worker_base_with_additional_roles(
56
+ message_libs.arr.span(), price_feed, role_admin, admins.arr.span(),
57
+ );
58
+
59
+ // Check that role_admin is only the role admin
60
+ assert(access_control.has_role(DEFAULT_ADMIN_ROLE, role_admin), 'Role admin should be set');
61
+ assert(!access_control.has_role(ADMIN_ROLE, role_admin), 'Role admin should not be admin');
62
+ assert!(
63
+ !access_control.has_role(MESSAGE_LIB_ROLE, role_admin), "Role admin should not be msg lib",
64
+ );
65
+
66
+ // Check that message libs are only message libs
67
+ for message_lib in message_libs.arr {
68
+ assert(access_control.has_role(MESSAGE_LIB_ROLE, message_lib), 'Msg lib should be set');
69
+ assert(!access_control.has_role(ADMIN_ROLE, message_lib), 'Msg lib should not be admin');
70
+ assert!(
71
+ !access_control.has_role(DEFAULT_ADMIN_ROLE, message_lib),
72
+ "Msg lib should not be role admin",
73
+ );
74
+ }
75
+
76
+ // Check that admins are only admins
77
+ for admin in admins.arr {
78
+ assert(access_control.has_role(ADMIN_ROLE, admin), 'Admin should be set');
79
+ assert(!access_control.has_role(MESSAGE_LIB_ROLE, admin), 'Admin should not be msg lib');
80
+ assert(
81
+ !access_control.has_role(DEFAULT_ADMIN_ROLE, admin), 'Admin should not be role admin',
82
+ );
83
+ }
84
+ }
85
+
86
+ #[test]
87
+ #[fuzzer(runs: 10)]
88
+ fn does_not_set_zero_admin(
89
+ price_feed: ContractAddress,
90
+ message_libs: ContractAddressArrayList,
91
+ admins: ContractAddressArrayList,
92
+ ) {
93
+ let WorkerBaseMock {
94
+ access_control, ..,
95
+ } =
96
+ deploy_worker_base_with_additional_roles(
97
+ message_libs.arr.span(), price_feed, ZERO_ADDRESS, admins.arr.span(),
98
+ );
99
+
100
+ // Check that zero address is not set as any role
101
+ assert!(
102
+ !access_control.has_role(DEFAULT_ADMIN_ROLE, ZERO_ADDRESS),
103
+ "Zero address should not be role admin",
104
+ );
105
+ assert!(!access_control.has_role(ADMIN_ROLE, ZERO_ADDRESS), "Zero address should not be admin");
106
+ assert!(
107
+ !access_control.has_role(MESSAGE_LIB_ROLE, ZERO_ADDRESS),
108
+ "Zero address should not be msg lib",
109
+ );
110
+
111
+ // Check that message libs are only message libs
112
+ for message_lib in message_libs.arr {
113
+ assert(access_control.has_role(MESSAGE_LIB_ROLE, message_lib), 'Msg lib should be set');
114
+ assert(!access_control.has_role(ADMIN_ROLE, message_lib), 'Msg lib should not be admin');
115
+ assert!(
116
+ !access_control.has_role(DEFAULT_ADMIN_ROLE, message_lib),
117
+ "Msg lib should not be role admin",
118
+ );
119
+ }
120
+
121
+ // Check that admins are only admins
122
+ for admin in admins.arr {
123
+ assert(access_control.has_role(ADMIN_ROLE, admin), 'Admin should be set');
124
+ assert(!access_control.has_role(MESSAGE_LIB_ROLE, admin), 'Admin should not be msg lib');
125
+ assert(
126
+ !access_control.has_role(DEFAULT_ADMIN_ROLE, admin), 'Admin should not be role admin',
127
+ );
128
+ }
129
+ }
130
+
131
+ #[test]
132
+ #[fuzzer(runs: 10)]
133
+ fn set_initial_price_feed_succeeds(role_admin: ContractAddress, price_feed: ContractAddress) {
134
+ let WorkerBaseMock { dispatcher, .. } = deploy_worker_base(price_feed, role_admin);
135
+ assert(dispatcher.get_price_feed() == price_feed, 'Price feed should be set');
136
+ }
137
+
138
+ //////////////////////
139
+ // Price feed tests //
140
+ //////////////////////
141
+
142
+ #[test]
143
+ #[fuzzer(runs: 10)]
144
+ fn set_price_feed_succeeds_when_admin(
145
+ role_admin: RoleAdmin,
146
+ admin: ContractAddress,
147
+ price_feed: ContractAddress,
148
+ new_price_feed: ContractAddress,
149
+ ) {
150
+ let role_admin = role_admin.address;
151
+ let WorkerBaseMock {
152
+ worker, dispatcher, ..,
153
+ } =
154
+ deploy_worker_base_with_additional_roles(
155
+ array![].span(), price_feed, role_admin, array![admin].span(),
156
+ );
157
+
158
+ // Set price feed
159
+ // Caller has admin role
160
+ start_cheat_caller_address(worker, admin);
161
+ dispatcher.set_price_feed(new_price_feed);
162
+ stop_cheat_caller_address(worker);
163
+
164
+ // Check that price feed is set
165
+ assert(dispatcher.get_price_feed() == new_price_feed, 'Price feed should be set');
166
+ }
167
+
168
+ #[test]
169
+ #[fuzzer(runs: 10)]
170
+ #[feature("safe_dispatcher")]
171
+ fn set_price_feed_fails_when_not_admin(
172
+ role_admin: RoleAdmin, not_admin: ContractAddress, price_feed: ContractAddress,
173
+ ) {
174
+ let role_admin = role_admin.address;
175
+ let WorkerBaseMock {
176
+ worker, safe_dispatcher, access_control, ..,
177
+ } = deploy_worker_base(price_feed, role_admin);
178
+
179
+ // Check that not_admin does not have the admin role
180
+ assert(!access_control.has_role(ADMIN_ROLE, not_admin), 'Should not have admin role');
181
+
182
+ // Attempt to set price feed
183
+ // Caller does not have admin role
184
+ start_cheat_caller_address(worker, role_admin);
185
+ let res = safe_dispatcher.set_price_feed(price_feed);
186
+ stop_cheat_caller_address(worker);
187
+
188
+ // Should panic with missing role error because role_admin does not have admin role
189
+ assert_panic_with_felt_error(res, AccessControlComponent::Errors::MISSING_ROLE);
190
+
191
+ // Attempt to set price feed
192
+ // Caller does not have admin role
193
+ start_cheat_caller_address(worker, not_admin);
194
+ let res = safe_dispatcher.set_price_feed(price_feed);
195
+ stop_cheat_caller_address(worker);
196
+
197
+ // Should panic with missing role error because not_admin does not have admin role
198
+ assert_panic_with_felt_error(res, AccessControlComponent::Errors::MISSING_ROLE);
199
+ }
200
+
201
+ #[test]
202
+ #[fuzzer(runs: 10)]
203
+ #[feature("safe_dispatcher")]
204
+ fn set_price_feed_fails_when_msg_lib(
205
+ role_admin: RoleAdmin, msg_lib: ContractAddress, price_feed: ContractAddress,
206
+ ) {
207
+ let role_admin = role_admin.address;
208
+ let WorkerBaseMock {
209
+ worker, safe_dispatcher, ..,
210
+ } =
211
+ deploy_worker_base_with_additional_roles(
212
+ array![msg_lib].span(), price_feed, role_admin, array![].span(),
213
+ );
214
+
215
+ // Attempt to set price feed
216
+ // Caller has msg lib role
217
+ start_cheat_caller_address(worker, msg_lib);
218
+ let res = safe_dispatcher.set_price_feed(price_feed);
219
+ stop_cheat_caller_address(worker);
220
+
221
+ // Should panic with missing role error because msg_lib does not have admin role
222
+ assert_panic_with_felt_error(res, AccessControlComponent::Errors::MISSING_ROLE);
223
+ }
224
+
225
+ #[test]
226
+ #[fuzzer(runs: 10)]
227
+ fn set_multiple_price_feeds_succeeds(
228
+ role_admin: RoleAdmin,
229
+ admin: ContractAddress,
230
+ price_feed: ContractAddress,
231
+ first_new_price_feed: ContractAddress,
232
+ second_new_price_feed: ContractAddress,
233
+ ) {
234
+ let role_admin = role_admin.address;
235
+ let WorkerBaseMock {
236
+ worker, dispatcher, ..,
237
+ } =
238
+ deploy_worker_base_with_additional_roles(
239
+ array![].span(), price_feed, role_admin, array![admin].span(),
240
+ );
241
+
242
+ // Caller has admin role
243
+ start_cheat_caller_address(worker, admin);
244
+
245
+ // First price feed update
246
+ dispatcher.set_price_feed(first_new_price_feed);
247
+ let price_feed = dispatcher.get_price_feed();
248
+ assert(price_feed == first_new_price_feed, 'First update should succeed');
249
+
250
+ // Second price feed update
251
+ dispatcher.set_price_feed(second_new_price_feed);
252
+ let price_feed = dispatcher.get_price_feed();
253
+ assert(price_feed == second_new_price_feed, 'Second update should succeed');
254
+
255
+ stop_cheat_caller_address(worker);
256
+ }
257
+
258
+ /////////////////////////////////
259
+ // Supported option type tests //
260
+ /////////////////////////////////
261
+
262
+ /// An admin can set the supported option type for an EID - corresponding event is emitted
263
+ #[test]
264
+ #[fuzzer(runs: 10)]
265
+ fn set_supported_option_type_succeeds_when_admin(
266
+ role_admin: RoleAdmin,
267
+ price_feed: ContractAddress,
268
+ admin: ContractAddress,
269
+ eid: Eid,
270
+ option_type: ByteArray,
271
+ ) {
272
+ let eid = eid.eid;
273
+ let role_admin = role_admin.address;
274
+ let WorkerBaseMock {
275
+ worker, dispatcher, ..,
276
+ } =
277
+ deploy_worker_base_with_additional_roles(
278
+ array![].span(), price_feed, role_admin, array![admin].span(),
279
+ );
280
+
281
+ let mut spy = spy_events();
282
+
283
+ // Caller has admin role
284
+ start_cheat_caller_address(worker, admin);
285
+ dispatcher.set_supported_option_type(eid, option_type.clone());
286
+ stop_cheat_caller_address(worker);
287
+
288
+ // Check that supported option type is set
289
+ assert!(
290
+ dispatcher.get_supported_option_type(eid) == option_type,
291
+ "Supported option type should be set",
292
+ );
293
+
294
+ // Verify SupportedOptionTypeSet event was emitted
295
+ let expected_event = WorkerBaseComponent::Event::SupportedOptionTypeSet(
296
+ SupportedOptionTypeSet { eid, option_type },
297
+ );
298
+ spy.assert_emitted(@array![(worker, expected_event)]);
299
+ }
300
+
301
+ /// A non-admin cannot set the supported option type for an EID - no event is emitted
302
+ #[test]
303
+ #[fuzzer(runs: 10)]
304
+ #[feature("safe_dispatcher")]
305
+ fn set_supported_option_type_fails_when_not_admin(
306
+ role_admin: RoleAdmin,
307
+ price_feed: ContractAddress,
308
+ not_admin: ContractAddress,
309
+ eid: Eid,
310
+ option_type: ByteArray,
311
+ ) {
312
+ let eid = eid.eid;
313
+ let role_admin = role_admin.address;
314
+ let WorkerBaseMock { worker, safe_dispatcher, .. } = deploy_worker_base(price_feed, role_admin);
315
+
316
+ let mut spy = spy_events();
317
+
318
+ // Caller does not have admin role
319
+ start_cheat_caller_address(worker, not_admin);
320
+ let res = safe_dispatcher.set_supported_option_type(eid, option_type.clone());
321
+ stop_cheat_caller_address(worker);
322
+
323
+ // Should panic with missing role error because not_admin does not have admin role
324
+ assert_panic_with_felt_error(res, AccessControlComponent::Errors::MISSING_ROLE);
325
+
326
+ // Check that supported option type is not set
327
+ assert!(
328
+ safe_dispatcher.get_supported_option_type(eid).unwrap() == Default::default(),
329
+ "Supported option type should not be set",
330
+ );
331
+
332
+ // Verify SupportedOptionTypeSet event was not emitted
333
+ let event = WorkerBaseComponent::Event::SupportedOptionTypeSet(
334
+ SupportedOptionTypeSet { eid, option_type },
335
+ );
336
+ spy.assert_not_emitted(@array![(worker, event)]);
337
+ }
338
+
339
+ //////////////////////////
340
+ // Access control tests //
341
+ //////////////////////////
342
+
343
+ /// The role admin can grant all roles
344
+ #[test]
345
+ #[fuzzer(runs: 10)]
346
+ fn access_control_grant_all_roles_succeed_when_role_admin(
347
+ role_admin: RoleAdmin,
348
+ new_role_admin: ContractAddress,
349
+ admin: ContractAddress,
350
+ msg_lib: ContractAddress,
351
+ price_feed: ContractAddress,
352
+ ) {
353
+ let role_admin = role_admin.address;
354
+ let WorkerBaseMock { worker, access_control, .. } = deploy_worker_base(price_feed, role_admin);
355
+
356
+ // Role admin grants new role admin
357
+ // Caller is the role admin
358
+ start_cheat_caller_address(worker, role_admin);
359
+ access_control.grant_role(DEFAULT_ADMIN_ROLE, new_role_admin);
360
+ stop_cheat_caller_address(worker);
361
+
362
+ // Check that new role admin is a role admin
363
+ assert(
364
+ access_control.has_role(DEFAULT_ADMIN_ROLE, new_role_admin), 'New role admin should be set',
365
+ );
366
+ assert!(
367
+ !access_control.has_role(ADMIN_ROLE, new_role_admin), "New role admin should not be admin",
368
+ );
369
+ assert!(
370
+ !access_control.has_role(MESSAGE_LIB_ROLE, new_role_admin),
371
+ "New role admin should not be msg lib",
372
+ );
373
+
374
+ // Role admin grants admin role
375
+ // Caller is the new role admin
376
+ start_cheat_caller_address(worker, role_admin);
377
+ access_control.grant_role(ADMIN_ROLE, admin);
378
+ stop_cheat_caller_address(worker);
379
+
380
+ // Check that admin only has the admin role
381
+ assert(access_control.has_role(ADMIN_ROLE, admin), 'Admin role should be set');
382
+ assert(!access_control.has_role(DEFAULT_ADMIN_ROLE, admin), 'Admin should not be role admin');
383
+ assert(!access_control.has_role(MESSAGE_LIB_ROLE, admin), 'Admin should not be msg lib');
384
+
385
+ // Role admin grants msg lib role
386
+ // Caller is the role admin
387
+ start_cheat_caller_address(worker, new_role_admin);
388
+ access_control.grant_role(MESSAGE_LIB_ROLE, msg_lib);
389
+ stop_cheat_caller_address(worker);
390
+
391
+ // Check that msg_lib has only the message lib role
392
+ assert!(access_control.has_role(MESSAGE_LIB_ROLE, msg_lib), "Msg lib role should be set");
393
+ assert!(!access_control.has_role(ADMIN_ROLE, msg_lib), "Msg lib should not be admin");
394
+ assert!(
395
+ !access_control.has_role(DEFAULT_ADMIN_ROLE, msg_lib), "Msg lib should not be role admin",
396
+ );
397
+ }
398
+
399
+ /// Only the role admin can grant roles
400
+ #[test]
401
+ #[fuzzer(runs: 10)]
402
+ #[feature("safe_dispatcher")]
403
+ fn access_control_grant_roles_fail_when_not_role_admin(
404
+ role_admin: ContractAddress,
405
+ not_role_admin: ContractAddress,
406
+ price_feed: ContractAddress,
407
+ grantee: ContractAddress,
408
+ message_libs: ContractAddressArrayList,
409
+ admins: ContractAddressArrayList,
410
+ ) {
411
+ // Ensure role_admin and not_role_admin are different
412
+ if role_admin == not_role_admin {
413
+ return;
414
+ }
415
+
416
+ let WorkerBaseMock {
417
+ worker, access_control, safe_access_control, ..,
418
+ } =
419
+ deploy_worker_base_with_additional_roles(
420
+ message_libs.arr.span(), price_feed, role_admin, admins.arr.span(),
421
+ );
422
+
423
+ // Attempt to grant admin role
424
+ // Caller is not the role admin
425
+ start_cheat_caller_address(worker, not_role_admin);
426
+ let res = safe_access_control.grant_role(ADMIN_ROLE, grantee);
427
+ stop_cheat_caller_address(worker);
428
+
429
+ // Should panic with missing role error because not_role_admin is not a role admin
430
+ assert_panic_with_felt_error(res, AccessControlComponent::Errors::MISSING_ROLE);
431
+ assert(!access_control.has_role(ADMIN_ROLE, grantee), 'Admin role should not be set');
432
+
433
+ // Attempt to grant message lib role
434
+ // Caller is not the role admin
435
+ start_cheat_caller_address(worker, not_role_admin);
436
+ let res = safe_access_control.grant_role(MESSAGE_LIB_ROLE, grantee);
437
+ stop_cheat_caller_address(worker);
438
+
439
+ // Should panic with missing role error because not_role_admin is not a role admin
440
+ assert_panic_with_felt_error(res, AccessControlComponent::Errors::MISSING_ROLE);
441
+ assert(!access_control.has_role(MESSAGE_LIB_ROLE, grantee), 'Msg lib role should not be set');
442
+ }
443
+
444
+ /// Admins cannot grant roles
445
+ #[test]
446
+ #[fuzzer(runs: 10)]
447
+ #[feature("safe_dispatcher")]
448
+ fn access_control_grant_roles_fail_when_admin(
449
+ role_admin: ContractAddress,
450
+ price_feed: ContractAddress,
451
+ grantee: ContractAddress,
452
+ admins: ContractAddressArrayList,
453
+ ) {
454
+ let WorkerBaseMock {
455
+ worker, access_control, safe_access_control, ..,
456
+ } =
457
+ deploy_worker_base_with_additional_roles(
458
+ array![].span(), price_feed, role_admin, admins.arr.span(),
459
+ );
460
+
461
+ for admin in admins.arr {
462
+ // Ensure that admin and role_admin are different
463
+ if admin == role_admin {
464
+ continue;
465
+ }
466
+
467
+ // Attempt to grant admin role
468
+ // Caller has admin role
469
+ start_cheat_caller_address(worker, admin);
470
+ let res = safe_access_control.grant_role(ADMIN_ROLE, grantee);
471
+
472
+ // Should panic with missing role error because admin is not a role admin
473
+ assert_panic_with_felt_error(res, AccessControlComponent::Errors::MISSING_ROLE);
474
+ assert(!access_control.has_role(ADMIN_ROLE, grantee), 'Admin role should not be set');
475
+
476
+ // Attempt to grant message lib role
477
+ let res = safe_access_control.grant_role(MESSAGE_LIB_ROLE, grantee);
478
+ stop_cheat_caller_address(worker);
479
+
480
+ // Should panic with missing role error because admin is not a role admin
481
+ assert_panic_with_felt_error(res, AccessControlComponent::Errors::MISSING_ROLE);
482
+ assert(
483
+ !access_control.has_role(MESSAGE_LIB_ROLE, grantee), 'Msg lib role should not be set',
484
+ );
485
+ }
486
+ }
487
+
488
+ /// Message libs cannot grant roles
489
+ #[test]
490
+ #[fuzzer(runs: 10)]
491
+ #[feature("safe_dispatcher")]
492
+ fn access_control_grant_roles_fail_when_message_lib(
493
+ role_admin: ContractAddress,
494
+ price_feed: ContractAddress,
495
+ grantee: ContractAddress,
496
+ message_libs: ContractAddressArrayList,
497
+ ) {
498
+ let WorkerBaseMock {
499
+ worker, access_control, safe_access_control, ..,
500
+ } =
501
+ deploy_worker_base_with_additional_roles(
502
+ message_libs.arr.span(), price_feed, role_admin, array![].span(),
503
+ );
504
+
505
+ for msg_lib in message_libs.arr {
506
+ // Ensure that msg_lib and role_admin are different
507
+ if msg_lib == role_admin {
508
+ continue;
509
+ }
510
+
511
+ // Attempt to grant admin role
512
+ // Caller has msg lib role
513
+ start_cheat_caller_address(worker, msg_lib);
514
+ let res = safe_access_control.grant_role(ADMIN_ROLE, grantee);
515
+
516
+ // Should panic with missing role error because msg_lib is not a role admin
517
+ assert_panic_with_felt_error(res, AccessControlComponent::Errors::MISSING_ROLE);
518
+ assert(!access_control.has_role(ADMIN_ROLE, grantee), 'Admin role should not be set');
519
+
520
+ // Attempt to grant message lib role
521
+ let res = safe_access_control.grant_role(MESSAGE_LIB_ROLE, grantee);
522
+ stop_cheat_caller_address(worker);
523
+
524
+ // Should panic with missing role error because msg_lib is not a role admin
525
+ assert_panic_with_felt_error(res, AccessControlComponent::Errors::MISSING_ROLE);
526
+ assert(
527
+ !access_control.has_role(MESSAGE_LIB_ROLE, grantee), 'Msg lib role should not be set',
528
+ );
529
+ }
530
+ }
531
+
532
+ /// A role admin can grant a new role admin which can then grant admin roles
533
+ #[test]
534
+ #[fuzzer(runs: 10)]
535
+ fn access_control_grant_role_chain_succeeds(
536
+ role_admin: ContractAddress,
537
+ new_role_admin: ContractAddress,
538
+ admin: ContractAddress,
539
+ msg_lib: ContractAddress,
540
+ price_feed: ContractAddress,
541
+ ) {
542
+ let WorkerBaseMock { worker, access_control, .. } = deploy_worker_base(price_feed, role_admin);
543
+
544
+ // Role admin grants admin role
545
+ // Caller is the role admin
546
+ start_cheat_caller_address(worker, role_admin);
547
+ access_control.grant_role(DEFAULT_ADMIN_ROLE, new_role_admin);
548
+ stop_cheat_caller_address(worker);
549
+
550
+ // Check that new_role_admin is the new role admin
551
+ assert(
552
+ access_control.has_role(DEFAULT_ADMIN_ROLE, new_role_admin), 'New role admin should be set',
553
+ );
554
+
555
+ // New role admin grants admin role
556
+ // Caller is a role admin
557
+ start_cheat_caller_address(worker, new_role_admin);
558
+ access_control.grant_role(ADMIN_ROLE, admin);
559
+ stop_cheat_caller_address(worker);
560
+
561
+ // Check that admin has the admin role
562
+ assert(access_control.has_role(ADMIN_ROLE, admin), 'Admin role should be set');
563
+
564
+ // New role admin grants message lib role
565
+ // Caller is a role admin
566
+ start_cheat_caller_address(worker, new_role_admin);
567
+ access_control.grant_role(MESSAGE_LIB_ROLE, msg_lib);
568
+ stop_cheat_caller_address(worker);
569
+
570
+ // Check that msg_lib has the message lib role
571
+ assert(access_control.has_role(MESSAGE_LIB_ROLE, msg_lib), 'Msg lib role should be set');
572
+ }
573
+
574
+ #[test]
575
+ #[fuzzer(runs: 10)]
576
+ fn access_control_role_admin_is_default(role_admin: RoleAdmin, price_feed: ContractAddress) {
577
+ let WorkerBaseMock { access_control, .. } = deploy_worker_base(price_feed, role_admin.address);
578
+ assert!(
579
+ access_control.get_role_admin(DEFAULT_ADMIN_ROLE) == DEFAULT_ADMIN_ROLE,
580
+ "Role admin is self admin",
581
+ );
582
+ assert!(
583
+ access_control.get_role_admin(ADMIN_ROLE) == DEFAULT_ADMIN_ROLE,
584
+ "Role admin is admin admin",
585
+ );
586
+ assert!(
587
+ access_control.get_role_admin(MESSAGE_LIB_ROLE) == DEFAULT_ADMIN_ROLE,
588
+ "Role admin is msg lib admin",
589
+ );
590
+ }
591
+
592
+ #[test]
593
+ #[fuzzer(runs: 10)]
594
+ fn access_control_revoking_roles_succeeds(
595
+ role_admin: RoleAdmin, admin: ContractAddress, msg_lib: ContractAddress,
596
+ ) {
597
+ let role_admin = role_admin.address;
598
+ let WorkerBaseMock {
599
+ worker, access_control, ..,
600
+ } =
601
+ deploy_worker_base_with_additional_roles(
602
+ array![msg_lib].span(), ZERO_ADDRESS, role_admin, array![admin].span(),
603
+ );
604
+
605
+ // Role admin revokes admin role
606
+ start_cheat_caller_address(worker, role_admin);
607
+ access_control.revoke_role(ADMIN_ROLE, admin);
608
+ stop_cheat_caller_address(worker);
609
+
610
+ // Check that admin does not have the admin role
611
+ assert(!access_control.has_role(ADMIN_ROLE, admin), 'Admin role should not be set');
612
+
613
+ // Role admin revokes message lib role
614
+ start_cheat_caller_address(worker, role_admin);
615
+ access_control.revoke_role(MESSAGE_LIB_ROLE, msg_lib);
616
+ stop_cheat_caller_address(worker);
617
+
618
+ // Check that msg_lib does not have the message lib role
619
+ assert(!access_control.has_role(MESSAGE_LIB_ROLE, msg_lib), 'Msg lib role should not be set');
620
+ }
621
+
622
+ ////////////////////////////////
623
+ // Set default multiplier bps //
624
+ ////////////////////////////////
625
+
626
+ /// An admin can set the default multiplier basis points - corresponding event is emitted
627
+ #[test]
628
+ #[fuzzer(runs: 10)]
629
+ fn set_default_multiplier_bps_succeeds_when_admin(
630
+ role_admin: RoleAdmin,
631
+ admin: ContractAddress,
632
+ price_feed: ContractAddress,
633
+ default_multiplier_bps: u16,
634
+ ) {
635
+ let role_admin = role_admin.address;
636
+ let WorkerBaseMock {
637
+ worker, dispatcher, ..,
638
+ } =
639
+ deploy_worker_base_with_additional_roles(
640
+ array![].span(), price_feed, role_admin, array![admin].span(),
641
+ );
642
+
643
+ let mut spy = spy_events();
644
+
645
+ // Caller has admin role
646
+ start_cheat_caller_address(worker, admin);
647
+ dispatcher.set_default_multiplier_bps(default_multiplier_bps);
648
+ stop_cheat_caller_address(worker);
649
+
650
+ // Check that default multiplier bps is set
651
+ assert!(
652
+ dispatcher.get_default_multiplier_bps() == default_multiplier_bps,
653
+ "Default multiplier bps should be set",
654
+ );
655
+
656
+ // Verify DefaultMultiplierBpsSet event was emitted
657
+ let expected_event = WorkerBaseComponent::Event::DefaultMultiplierBpsSet(
658
+ DefaultMultiplierBpsSet { default_multiplier_bps },
659
+ );
660
+ spy.assert_emitted(@array![(worker, expected_event)]);
661
+ }
662
+
663
+ /// A non-admin cannot set the default multiplier basis points - no event is emitted
664
+ #[test]
665
+ #[fuzzer(runs: 10)]
666
+ #[feature("safe_dispatcher")]
667
+ fn set_default_multiplier_bps_fails_when_not_admin(
668
+ role_admin: RoleAdmin,
669
+ price_feed: ContractAddress,
670
+ not_admin: ContractAddress,
671
+ default_multiplier_bps: u16,
672
+ ) {
673
+ let role_admin = role_admin.address;
674
+ let WorkerBaseMock { worker, safe_dispatcher, .. } = deploy_worker_base(price_feed, role_admin);
675
+
676
+ let mut spy = spy_events();
677
+
678
+ // Caller does not have admin role
679
+ start_cheat_caller_address(worker, not_admin);
680
+ let res = safe_dispatcher.set_default_multiplier_bps(default_multiplier_bps);
681
+ stop_cheat_caller_address(worker);
682
+
683
+ // Should panic with missing role error because not_admin does not have admin role
684
+ assert_panic_with_felt_error(res, AccessControlComponent::Errors::MISSING_ROLE);
685
+
686
+ // Verify DefaultMultiplierBpsSet event was not emitted
687
+ let event = WorkerBaseComponent::Event::DefaultMultiplierBpsSet(
688
+ DefaultMultiplierBpsSet { default_multiplier_bps },
689
+ );
690
+ spy.assert_not_emitted(@array![(worker, event)]);
691
+ }
692
+
693
+ ////////////////////////
694
+ // Withdraw fee tests //
695
+ ////////////////////////
696
+
697
+ /// Admin can withdraw fee
698
+ #[test]
699
+ #[fuzzer(runs: 10)]
700
+ fn withdraw_fee_succeeds_when_correct(
701
+ role_admin: RoleAdmin,
702
+ admin: ContractAddress,
703
+ price_feed: ContractAddress,
704
+ token_holder: ContractAddress,
705
+ recipient: ContractAddress,
706
+ amount: u256,
707
+ ) {
708
+ let role_admin = role_admin.address;
709
+ let WorkerBaseMock {
710
+ worker, dispatcher, ..,
711
+ } =
712
+ deploy_worker_base_with_additional_roles(
713
+ array![].span(), price_feed, role_admin, array![admin].span(),
714
+ );
715
+
716
+ // Give token_holder ERC20 tokens
717
+ const MIN_AMOUNT: u256 = 1_000;
718
+ const MAX_AMOUNT: u256 = 9_000;
719
+ let amount = MIN_AMOUNT + amount % MAX_AMOUNT;
720
+ let ERC20Mock { token, token_dispatcher } = deploy_mock_erc20(amount, token_holder);
721
+
722
+ // Holder transfers tokens to worker
723
+ // Caller has tokens
724
+ start_cheat_caller_address(token, token_holder);
725
+ token_dispatcher.transfer(worker, amount);
726
+ stop_cheat_caller_address(token);
727
+
728
+ // Check that worker received tokens
729
+ assert(token_dispatcher.balance_of(worker) == amount, 'Worker balance should be amount');
730
+
731
+ let mut spy = spy_events();
732
+
733
+ // Admin withdraws fee
734
+ // Caller has admin role
735
+ let withdraw_amount = amount / 2;
736
+ start_cheat_caller_address(worker, admin);
737
+ dispatcher.withdraw_fee(token, recipient, withdraw_amount);
738
+ stop_cheat_caller_address(worker);
739
+
740
+ // Verify balances
741
+ assert!(
742
+ token_dispatcher.balance_of(recipient) == withdraw_amount,
743
+ "Recipient balance should be withdraw amount",
744
+ );
745
+ assert!(
746
+ token_dispatcher.balance_of(worker) == amount - withdraw_amount,
747
+ "Worker balance should be remaining",
748
+ );
749
+
750
+ // Verify WithdrawFee event was emitted
751
+ let expected_event = WorkerBaseComponent::Event::FeeWithdrawn(
752
+ FeeWithdrawn { to: recipient, amount: withdraw_amount },
753
+ );
754
+ spy.assert_emitted(@array![(worker, expected_event)]);
755
+
756
+ // Verify ERC20 transfer event was emitted
757
+ let erc20_event = ERC20Event::Transfer(
758
+ Transfer { from: worker, to: recipient, value: withdraw_amount },
759
+ );
760
+ spy.assert_emitted(@array![(token, erc20_event)]);
761
+ }
762
+
763
+ /// Fees cannot be withdrawn if
764
+ /// - caller is not admin
765
+ /// - caller is admin but trying to withdraw more than worker balance
766
+ #[test]
767
+ #[fuzzer(runs: 10)]
768
+ #[feature("safe_dispatcher")]
769
+ fn withdraw_fee_fails_when_incorrect(
770
+ role_admin: RoleAdmin,
771
+ admin: ContractAddress,
772
+ not_admin: ContractAddress,
773
+ price_feed: ContractAddress,
774
+ token_holder: ContractAddress,
775
+ recipient: ContractAddress,
776
+ amount: u256,
777
+ ) {
778
+ // Ensure that admin and not_admin are different
779
+ if admin == not_admin {
780
+ return;
781
+ }
782
+
783
+ let role_admin = role_admin.address;
784
+ let WorkerBaseMock {
785
+ worker, safe_dispatcher, ..,
786
+ } =
787
+ deploy_worker_base_with_additional_roles(
788
+ array![].span(), price_feed, role_admin, array![admin].span(),
789
+ );
790
+
791
+ // Give token_holder ERC20 tokens
792
+ const MIN_AMOUNT: u256 = 1_000;
793
+ const MAX_AMOUNT: u256 = 9_000;
794
+ let amount = MIN_AMOUNT + amount % MAX_AMOUNT;
795
+ let ERC20Mock { token, token_dispatcher } = deploy_mock_erc20(amount, token_holder);
796
+
797
+ // Holder transfers tokens to worker
798
+ // Caller has tokens
799
+ start_cheat_caller_address(token, token_holder);
800
+ token_dispatcher.transfer(worker, amount);
801
+ stop_cheat_caller_address(token);
802
+
803
+ // Check that worker received tokens
804
+ assert(token_dispatcher.balance_of(worker) == amount, 'Worker balance should be amount');
805
+
806
+ ///////////////////////////////////
807
+ // Non-admin cannot withdraw fee //
808
+ ///////////////////////////////////
809
+
810
+ // Non-admin attempts to withdraw fee
811
+ // Caller does not have admin role
812
+ let withdraw_amount = amount / 2;
813
+ start_cheat_caller_address(worker, not_admin);
814
+ let res = safe_dispatcher.withdraw_fee(token, recipient, withdraw_amount);
815
+ stop_cheat_caller_address(worker);
816
+
817
+ // Should panic with missing role error because not_admin does not have the admin role
818
+ assert_panic_with_felt_error(res, AccessControlComponent::Errors::MISSING_ROLE);
819
+
820
+ // Verify balances are unchanged
821
+ assert(token_dispatcher.balance_of(recipient) == 0, 'Recipient balance should be 0');
822
+ assert(token_dispatcher.balance_of(worker) == amount, 'Worker balance should be amount');
823
+
824
+ //////////////////////////////////////////////
825
+ // Cannot withdraw more than worker balance //
826
+ //////////////////////////////////////////////
827
+
828
+ // Admin attempts to withdraw more than worker balance
829
+ let withdraw_amount = amount + 1;
830
+ start_cheat_caller_address(worker, admin);
831
+ let res = safe_dispatcher.withdraw_fee(token, recipient, withdraw_amount);
832
+ stop_cheat_caller_address(worker);
833
+
834
+ // Should panic with insufficient balance error because admin is trying to withdraw more than
835
+ // worker balance
836
+ assert_panic_with_felt_error(res, ERC20Errors::INSUFFICIENT_BALANCE);
837
+
838
+ // Verify balances are unchanged
839
+ assert(token_dispatcher.balance_of(recipient) == 0, 'Recipient balance should be 0');
840
+ assert(token_dispatcher.balance_of(worker) == amount, 'Worker balance should be amount');
841
+ }
842
+
843
+ ////////////////////////////
844
+ // Worker fee_lib tests //
845
+ ////////////////////////////
846
+
847
+ /// An admin can set the worker fee_lib address - corresponding event is emitted
848
+ #[test]
849
+ #[fuzzer(runs: 10)]
850
+ fn set_worker_fee_lib_succeeds_when_admin(
851
+ role_admin: RoleAdmin,
852
+ admin: ContractAddress,
853
+ price_feed: ContractAddress,
854
+ initial_worker_fee_lib: ContractAddress,
855
+ new_worker_fee_lib: ContractAddress,
856
+ ) {
857
+ let role_admin = role_admin.address;
858
+ let WorkerBaseMock {
859
+ worker, dispatcher, ..,
860
+ } =
861
+ deploy_worker_base_with_additional_roles(
862
+ array![].span(), price_feed, role_admin, array![admin].span(),
863
+ );
864
+
865
+ // Set initial worker fee_lib
866
+ start_cheat_caller_address(worker, admin);
867
+ dispatcher.set_worker_fee_lib(initial_worker_fee_lib);
868
+ stop_cheat_caller_address(worker);
869
+
870
+ let mut spy = spy_events();
871
+
872
+ // Set new worker fee_lib
873
+ start_cheat_caller_address(worker, admin);
874
+ dispatcher.set_worker_fee_lib(new_worker_fee_lib);
875
+ stop_cheat_caller_address(worker);
876
+
877
+ // Check that worker fee_lib is set
878
+ assert!(dispatcher.get_worker_fee_lib() == new_worker_fee_lib, "Worker fee_lib should be set");
879
+
880
+ // Verify WorkerFeeLibSet event was emitted
881
+ let expected_event = WorkerBaseComponent::Event::WorkerFeeLibSet(
882
+ WorkerFeeLibSet {
883
+ old_worker_fee_lib: initial_worker_fee_lib, new_worker_fee_lib: new_worker_fee_lib,
884
+ },
885
+ );
886
+ spy.assert_emitted(@array![(worker, expected_event)]);
887
+ }
888
+
889
+ /// A non-admin cannot set the worker fee_lib address - no event is emitted
890
+ #[test]
891
+ #[fuzzer(runs: 10)]
892
+ #[feature("safe_dispatcher")]
893
+ fn set_worker_fee_lib_fails_when_not_admin(
894
+ role_admin: RoleAdmin,
895
+ price_feed: ContractAddress,
896
+ not_admin: ContractAddress,
897
+ worker_fee_lib: ContractAddress,
898
+ ) {
899
+ let role_admin = role_admin.address;
900
+ let WorkerBaseMock { worker, safe_dispatcher, .. } = deploy_worker_base(price_feed, role_admin);
901
+
902
+ let mut spy = spy_events();
903
+
904
+ // Caller does not have admin role
905
+ start_cheat_caller_address(worker, not_admin);
906
+ let res = safe_dispatcher.set_worker_fee_lib(worker_fee_lib);
907
+ stop_cheat_caller_address(worker);
908
+
909
+ // Should panic with missing role error because not_admin does not have admin role
910
+ assert_panic_with_felt_error(res, AccessControlComponent::Errors::MISSING_ROLE);
911
+
912
+ // Check that worker fee_lib is not set (should be zero address by default)
913
+ assert!(
914
+ safe_dispatcher.get_worker_fee_lib().unwrap() == ZERO_ADDRESS,
915
+ "Worker fee_lib should not be set",
916
+ );
917
+
918
+ // Verify WorkerFeeLibSet event was not emitted
919
+ let event = WorkerBaseComponent::Event::WorkerFeeLibSet(
920
+ WorkerFeeLibSet { old_worker_fee_lib: ZERO_ADDRESS, new_worker_fee_lib: worker_fee_lib },
921
+ );
922
+ spy.assert_not_emitted(@array![(worker, event)]);
923
+ }
924
+
925
+ /// A message lib cannot set the worker fee_lib address
926
+ #[test]
927
+ #[fuzzer(runs: 10)]
928
+ #[feature("safe_dispatcher")]
929
+ fn set_worker_fee_lib_fails_when_msg_lib(
930
+ role_admin: RoleAdmin,
931
+ msg_lib: ContractAddress,
932
+ price_feed: ContractAddress,
933
+ worker_fee_lib: ContractAddress,
934
+ ) {
935
+ let role_admin = role_admin.address;
936
+ let WorkerBaseMock {
937
+ worker, safe_dispatcher, ..,
938
+ } =
939
+ deploy_worker_base_with_additional_roles(
940
+ array![msg_lib].span(), price_feed, role_admin, array![].span(),
941
+ );
942
+
943
+ // Attempt to set worker fee_lib
944
+ // Caller has msg lib role
945
+ start_cheat_caller_address(worker, msg_lib);
946
+ let res = safe_dispatcher.set_worker_fee_lib(worker_fee_lib);
947
+ stop_cheat_caller_address(worker);
948
+
949
+ // Should panic with missing role error because msg_lib does not have admin role
950
+ assert_panic_with_felt_error(res, AccessControlComponent::Errors::MISSING_ROLE);
951
+ }
952
+
953
+ /// Admin can set multiple worker fee_lib addresses successfully
954
+ #[test]
955
+ #[fuzzer(runs: 10)]
956
+ fn set_multiple_worker_fee_lib_succeeds(
957
+ role_admin: RoleAdmin,
958
+ admin: ContractAddress,
959
+ price_feed: ContractAddress,
960
+ first_worker_fee_lib: ContractAddress,
961
+ second_worker_fee_lib: ContractAddress,
962
+ ) {
963
+ let role_admin = role_admin.address;
964
+ let WorkerBaseMock {
965
+ worker, dispatcher, ..,
966
+ } =
967
+ deploy_worker_base_with_additional_roles(
968
+ array![].span(), price_feed, role_admin, array![admin].span(),
969
+ );
970
+
971
+ // Caller has admin role
972
+ start_cheat_caller_address(worker, admin);
973
+
974
+ // First worker fee_lib update
975
+ dispatcher.set_worker_fee_lib(first_worker_fee_lib);
976
+ let fee_lib = dispatcher.get_worker_fee_lib();
977
+ assert(fee_lib == first_worker_fee_lib, 'First update should succeed');
978
+
979
+ // Second worker fee_lib update
980
+ dispatcher.set_worker_fee_lib(second_worker_fee_lib);
981
+ let fee_lib = dispatcher.get_worker_fee_lib();
982
+ assert(fee_lib == second_worker_fee_lib, 'Second update should succeed');
983
+
984
+ stop_cheat_caller_address(worker);
985
+ }
986
+
987
+ /// Get worker fee_lib returns zero address by default
988
+ #[test]
989
+ #[fuzzer(runs: 10)]
990
+ fn get_worker_fee_lib_returns_zero_by_default(role_admin: RoleAdmin, price_feed: ContractAddress) {
991
+ let role_admin = role_admin.address;
992
+ let WorkerBaseMock { dispatcher, .. } = deploy_worker_base(price_feed, role_admin);
993
+
994
+ // Should return zero address by default
995
+ assert!(
996
+ dispatcher.get_worker_fee_lib() == ZERO_ADDRESS,
997
+ "Worker fee_lib should be zero address by default",
998
+ );
999
+ }
1000
+
1001
+ ////////////////////////////
1002
+ // Renouncing roles tests //
1003
+ ////////////////////////////
1004
+
1005
+ /// Renouncing roles is disabled
1006
+ #[test]
1007
+ #[fuzzer(runs: 10)]
1008
+ #[feature("safe_dispatcher")]
1009
+ fn renounce_roles_disabled(
1010
+ role_admin: RoleAdmin, admin: ContractAddress, price_feed: ContractAddress,
1011
+ ) {
1012
+ let role_admin = role_admin.address;
1013
+ let WorkerBaseMock {
1014
+ worker, access_control, safe_access_control, ..,
1015
+ } = deploy_worker_base(price_feed, role_admin);
1016
+
1017
+ // Caller is the role admin
1018
+ start_cheat_caller_address(worker, role_admin);
1019
+ let res = safe_access_control.renounce_role(DEFAULT_ADMIN_ROLE, role_admin);
1020
+ stop_cheat_caller_address(worker);
1021
+
1022
+ // Should panic with renouncing role error because role admin cannot renounce role
1023
+ assert_panic_with_error(res, errors::err_role_renouncing_disabled());
1024
+
1025
+ // Check that admin does not have the admin role
1026
+ assert(access_control.has_role(DEFAULT_ADMIN_ROLE, role_admin), 'Role should not be renounced');
1027
+ }
1028
+
1029
+ ////////////////////
1030
+ // Pausable tests //
1031
+ ////////////////////
1032
+
1033
+ /// Default admin can pause the contract
1034
+ #[test]
1035
+ #[fuzzer(runs: 10)]
1036
+ fn pause_succeeds_when_default_admin(role_admin: RoleAdmin, price_feed: ContractAddress) {
1037
+ let role_admin = role_admin.address;
1038
+ let WorkerBaseMock {
1039
+ worker, dispatcher, access_control, ..,
1040
+ } = deploy_worker_base(price_feed, role_admin);
1041
+
1042
+ let mut spy = spy_events();
1043
+
1044
+ // Caller has default admin role
1045
+ assert!(
1046
+ access_control.has_role(DEFAULT_ADMIN_ROLE, role_admin),
1047
+ "Role admin should have default admin role",
1048
+ );
1049
+ start_cheat_caller_address(worker, role_admin);
1050
+ dispatcher.pause();
1051
+ stop_cheat_caller_address(worker);
1052
+
1053
+ // Check that contract is paused
1054
+ let pausable_dispatcher = IPausableDispatcher { contract_address: worker };
1055
+ assert(pausable_dispatcher.is_paused(), 'Contract should be paused');
1056
+
1057
+ // Verify Paused event was emitted
1058
+ let expected_event = PausableComponent::Event::Paused(
1059
+ PausableComponent::Paused { account: role_admin },
1060
+ );
1061
+ spy.assert_emitted(@array![(worker, expected_event)]);
1062
+ }
1063
+
1064
+ /// Non-default admin cannot pause the contract
1065
+ #[test]
1066
+ #[fuzzer(runs: 10)]
1067
+ #[feature("safe_dispatcher")]
1068
+ fn pause_fails_when_not_default_admin(
1069
+ role_admin: RoleAdmin, price_feed: ContractAddress, not_default_admin: ContractAddress,
1070
+ ) {
1071
+ // Ensure that role_admin and not_default_admin are different
1072
+ let role_admin = role_admin.address;
1073
+ if role_admin == not_default_admin {
1074
+ return;
1075
+ }
1076
+
1077
+ let WorkerBaseMock {
1078
+ worker, safe_dispatcher, access_control, ..,
1079
+ } = deploy_worker_base(price_feed, role_admin);
1080
+
1081
+ // Caller does not have default admin role
1082
+ assert!(
1083
+ !access_control.has_role(DEFAULT_ADMIN_ROLE, not_default_admin),
1084
+ "Caller should not have default admin role",
1085
+ );
1086
+ start_cheat_caller_address(worker, not_default_admin);
1087
+ let res = safe_dispatcher.pause();
1088
+ stop_cheat_caller_address(worker);
1089
+
1090
+ // Check that contract is not paused
1091
+ let pausable_dispatcher = IPausableDispatcher { contract_address: worker };
1092
+ assert(!pausable_dispatcher.is_paused(), 'Contract should not be paused');
1093
+
1094
+ // Should panic with missing role error because not_default_admin does not have default admin
1095
+ // role
1096
+ assert_panic_with_felt_error(res, AccessControlComponent::Errors::MISSING_ROLE);
1097
+ }