@0xsequence/catapult 1.1.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 (393) hide show
  1. package/.eslintrc.json +29 -0
  2. package/.github/workflows/ci.yml +181 -0
  3. package/CONCEPT.md +24 -0
  4. package/README.md +772 -0
  5. package/contracts/checked-call.huff +65 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +16 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/commands/common.d.ts +11 -0
  11. package/dist/commands/common.d.ts.map +1 -0
  12. package/dist/commands/common.js +73 -0
  13. package/dist/commands/common.js.map +1 -0
  14. package/dist/commands/dry.d.ts +3 -0
  15. package/dist/commands/dry.d.ts.map +1 -0
  16. package/dist/commands/dry.js +171 -0
  17. package/dist/commands/dry.js.map +1 -0
  18. package/dist/commands/etherscan.d.ts +3 -0
  19. package/dist/commands/etherscan.d.ts.map +1 -0
  20. package/dist/commands/etherscan.js +323 -0
  21. package/dist/commands/etherscan.js.map +1 -0
  22. package/dist/commands/index.d.ts +6 -0
  23. package/dist/commands/index.d.ts.map +1 -0
  24. package/dist/commands/index.js +22 -0
  25. package/dist/commands/index.js.map +1 -0
  26. package/dist/commands/list.d.ts +3 -0
  27. package/dist/commands/list.d.ts.map +1 -0
  28. package/dist/commands/list.js +259 -0
  29. package/dist/commands/list.js.map +1 -0
  30. package/dist/commands/run.d.ts +3 -0
  31. package/dist/commands/run.d.ts.map +1 -0
  32. package/dist/commands/run.js +96 -0
  33. package/dist/commands/run.js.map +1 -0
  34. package/dist/commands/utils.d.ts +3 -0
  35. package/dist/commands/utils.d.ts.map +1 -0
  36. package/dist/commands/utils.js +46 -0
  37. package/dist/commands/utils.js.map +1 -0
  38. package/dist/index.d.ts +4 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +58 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/lib/__tests__/deployer-events.spec.d.ts +2 -0
  43. package/dist/lib/__tests__/deployer-events.spec.d.ts.map +1 -0
  44. package/dist/lib/__tests__/deployer-events.spec.js +260 -0
  45. package/dist/lib/__tests__/deployer-events.spec.js.map +1 -0
  46. package/dist/lib/__tests__/deployer.spec.d.ts +2 -0
  47. package/dist/lib/__tests__/deployer.spec.d.ts.map +1 -0
  48. package/dist/lib/__tests__/deployer.spec.js +884 -0
  49. package/dist/lib/__tests__/deployer.spec.js.map +1 -0
  50. package/dist/lib/__tests__/network-utils.spec.d.ts +2 -0
  51. package/dist/lib/__tests__/network-utils.spec.d.ts.map +1 -0
  52. package/dist/lib/__tests__/network-utils.spec.js +140 -0
  53. package/dist/lib/__tests__/network-utils.spec.js.map +1 -0
  54. package/dist/lib/contracts/__tests__/repository.spec.d.ts +2 -0
  55. package/dist/lib/contracts/__tests__/repository.spec.d.ts.map +1 -0
  56. package/dist/lib/contracts/__tests__/repository.spec.js +321 -0
  57. package/dist/lib/contracts/__tests__/repository.spec.js.map +1 -0
  58. package/dist/lib/contracts/repository.d.ts +27 -0
  59. package/dist/lib/contracts/repository.d.ts.map +1 -0
  60. package/dist/lib/contracts/repository.js +241 -0
  61. package/dist/lib/contracts/repository.js.map +1 -0
  62. package/dist/lib/core/__tests__/engine.spec.d.ts +2 -0
  63. package/dist/lib/core/__tests__/engine.spec.d.ts.map +1 -0
  64. package/dist/lib/core/__tests__/engine.spec.js +1212 -0
  65. package/dist/lib/core/__tests__/engine.spec.js.map +1 -0
  66. package/dist/lib/core/__tests__/graph.spec.d.ts +2 -0
  67. package/dist/lib/core/__tests__/graph.spec.d.ts.map +1 -0
  68. package/dist/lib/core/__tests__/graph.spec.js +116 -0
  69. package/dist/lib/core/__tests__/graph.spec.js.map +1 -0
  70. package/dist/lib/core/__tests__/json-integration.spec.d.ts +2 -0
  71. package/dist/lib/core/__tests__/json-integration.spec.d.ts.map +1 -0
  72. package/dist/lib/core/__tests__/json-integration.spec.js +300 -0
  73. package/dist/lib/core/__tests__/json-integration.spec.js.map +1 -0
  74. package/dist/lib/core/__tests__/loader.spec.d.ts +2 -0
  75. package/dist/lib/core/__tests__/loader.spec.d.ts.map +1 -0
  76. package/dist/lib/core/__tests__/loader.spec.js +288 -0
  77. package/dist/lib/core/__tests__/loader.spec.js.map +1 -0
  78. package/dist/lib/core/__tests__/multi-platform-verification.spec.d.ts +2 -0
  79. package/dist/lib/core/__tests__/multi-platform-verification.spec.d.ts.map +1 -0
  80. package/dist/lib/core/__tests__/multi-platform-verification.spec.js +342 -0
  81. package/dist/lib/core/__tests__/multi-platform-verification.spec.js.map +1 -0
  82. package/dist/lib/core/__tests__/resolver.spec.d.ts +2 -0
  83. package/dist/lib/core/__tests__/resolver.spec.d.ts.map +1 -0
  84. package/dist/lib/core/__tests__/resolver.spec.js +1367 -0
  85. package/dist/lib/core/__tests__/resolver.spec.js.map +1 -0
  86. package/dist/lib/core/__tests__/static-action.spec.d.ts +2 -0
  87. package/dist/lib/core/__tests__/static-action.spec.d.ts.map +1 -0
  88. package/dist/lib/core/__tests__/static-action.spec.js +136 -0
  89. package/dist/lib/core/__tests__/static-action.spec.js.map +1 -0
  90. package/dist/lib/core/context.d.ts +29 -0
  91. package/dist/lib/core/context.d.ts.map +1 -0
  92. package/dist/lib/core/context.js +88 -0
  93. package/dist/lib/core/context.js.map +1 -0
  94. package/dist/lib/core/engine.d.ts +25 -0
  95. package/dist/lib/core/engine.d.ts.map +1 -0
  96. package/dist/lib/core/engine.js +1191 -0
  97. package/dist/lib/core/engine.js.map +1 -0
  98. package/dist/lib/core/graph.d.ts +18 -0
  99. package/dist/lib/core/graph.d.ts.map +1 -0
  100. package/dist/lib/core/graph.js +158 -0
  101. package/dist/lib/core/graph.js.map +1 -0
  102. package/dist/lib/core/loader.d.ts +25 -0
  103. package/dist/lib/core/loader.d.ts.map +1 -0
  104. package/dist/lib/core/loader.js +248 -0
  105. package/dist/lib/core/loader.js.map +1 -0
  106. package/dist/lib/core/resolver.d.ts +20 -0
  107. package/dist/lib/core/resolver.d.ts.map +1 -0
  108. package/dist/lib/core/resolver.js +307 -0
  109. package/dist/lib/core/resolver.js.map +1 -0
  110. package/dist/lib/deployer.d.ts +39 -0
  111. package/dist/lib/deployer.d.ts.map +1 -0
  112. package/dist/lib/deployer.js +533 -0
  113. package/dist/lib/deployer.js.map +1 -0
  114. package/dist/lib/events/__tests__/event-system.spec.d.ts +2 -0
  115. package/dist/lib/events/__tests__/event-system.spec.d.ts.map +1 -0
  116. package/dist/lib/events/__tests__/event-system.spec.js +256 -0
  117. package/dist/lib/events/__tests__/event-system.spec.js.map +1 -0
  118. package/dist/lib/events/cli-adapter.d.ts +13 -0
  119. package/dist/lib/events/cli-adapter.d.ts.map +1 -0
  120. package/dist/lib/events/cli-adapter.js +244 -0
  121. package/dist/lib/events/cli-adapter.js.map +1 -0
  122. package/dist/lib/events/emitter.d.ts +11 -0
  123. package/dist/lib/events/emitter.d.ts.map +1 -0
  124. package/dist/lib/events/emitter.js +29 -0
  125. package/dist/lib/events/emitter.js.map +1 -0
  126. package/dist/lib/events/index.d.ts +4 -0
  127. package/dist/lib/events/index.d.ts.map +1 -0
  128. package/dist/lib/events/index.js +20 -0
  129. package/dist/lib/events/index.js.map +1 -0
  130. package/dist/lib/events/types.d.ts +368 -0
  131. package/dist/lib/events/types.d.ts.map +1 -0
  132. package/dist/lib/events/types.js +3 -0
  133. package/dist/lib/events/types.js.map +1 -0
  134. package/dist/lib/index.d.ts +5 -0
  135. package/dist/lib/index.d.ts.map +1 -0
  136. package/dist/lib/index.js +44 -0
  137. package/dist/lib/index.js.map +1 -0
  138. package/dist/lib/network-loader.d.ts +3 -0
  139. package/dist/lib/network-loader.d.ts.map +1 -0
  140. package/dist/lib/network-loader.js +80 -0
  141. package/dist/lib/network-loader.js.map +1 -0
  142. package/dist/lib/network-match.d.ts +3 -0
  143. package/dist/lib/network-match.d.ts.map +1 -0
  144. package/dist/lib/network-match.js +62 -0
  145. package/dist/lib/network-match.js.map +1 -0
  146. package/dist/lib/network-utils.d.ts +4 -0
  147. package/dist/lib/network-utils.d.ts.map +1 -0
  148. package/dist/lib/network-utils.js +39 -0
  149. package/dist/lib/network-utils.js.map +1 -0
  150. package/dist/lib/parsers/__tests__/buildinfo.spec.d.ts +2 -0
  151. package/dist/lib/parsers/__tests__/buildinfo.spec.d.ts.map +1 -0
  152. package/dist/lib/parsers/__tests__/buildinfo.spec.js +132 -0
  153. package/dist/lib/parsers/__tests__/buildinfo.spec.js.map +1 -0
  154. package/dist/lib/parsers/__tests__/job.spec.d.ts +2 -0
  155. package/dist/lib/parsers/__tests__/job.spec.d.ts.map +1 -0
  156. package/dist/lib/parsers/__tests__/job.spec.js +318 -0
  157. package/dist/lib/parsers/__tests__/job.spec.js.map +1 -0
  158. package/dist/lib/parsers/__tests__/template.spec.d.ts +2 -0
  159. package/dist/lib/parsers/__tests__/template.spec.d.ts.map +1 -0
  160. package/dist/lib/parsers/__tests__/template.spec.js +126 -0
  161. package/dist/lib/parsers/__tests__/template.spec.js.map +1 -0
  162. package/dist/lib/parsers/artifact/__tests__/artifact.spec.d.ts +2 -0
  163. package/dist/lib/parsers/artifact/__tests__/artifact.spec.d.ts.map +1 -0
  164. package/dist/lib/parsers/artifact/__tests__/artifact.spec.js +128 -0
  165. package/dist/lib/parsers/artifact/__tests__/artifact.spec.js.map +1 -0
  166. package/dist/lib/parsers/artifact/foundry-1.2.d.ts +3 -0
  167. package/dist/lib/parsers/artifact/foundry-1.2.d.ts.map +1 -0
  168. package/dist/lib/parsers/artifact/foundry-1.2.js +82 -0
  169. package/dist/lib/parsers/artifact/foundry-1.2.js.map +1 -0
  170. package/dist/lib/parsers/artifact/index.d.ts +3 -0
  171. package/dist/lib/parsers/artifact/index.d.ts.map +1 -0
  172. package/dist/lib/parsers/artifact/index.js +17 -0
  173. package/dist/lib/parsers/artifact/index.js.map +1 -0
  174. package/dist/lib/parsers/artifact/types.d.ts +3 -0
  175. package/dist/lib/parsers/artifact/types.d.ts.map +1 -0
  176. package/dist/lib/parsers/artifact/types.js +3 -0
  177. package/dist/lib/parsers/artifact/types.js.map +1 -0
  178. package/dist/lib/parsers/buildinfo.d.ts +5 -0
  179. package/dist/lib/parsers/buildinfo.d.ts.map +1 -0
  180. package/dist/lib/parsers/buildinfo.js +85 -0
  181. package/dist/lib/parsers/buildinfo.js.map +1 -0
  182. package/dist/lib/parsers/constants.d.ts +4 -0
  183. package/dist/lib/parsers/constants.d.ts.map +1 -0
  184. package/dist/lib/parsers/constants.js +45 -0
  185. package/dist/lib/parsers/constants.js.map +1 -0
  186. package/dist/lib/parsers/index.d.ts +5 -0
  187. package/dist/lib/parsers/index.d.ts.map +1 -0
  188. package/dist/lib/parsers/index.js +21 -0
  189. package/dist/lib/parsers/index.js.map +1 -0
  190. package/dist/lib/parsers/job.d.ts +3 -0
  191. package/dist/lib/parsers/job.d.ts.map +1 -0
  192. package/dist/lib/parsers/job.js +74 -0
  193. package/dist/lib/parsers/job.js.map +1 -0
  194. package/dist/lib/parsers/template.d.ts +3 -0
  195. package/dist/lib/parsers/template.d.ts.map +1 -0
  196. package/dist/lib/parsers/template.js +91 -0
  197. package/dist/lib/parsers/template.js.map +1 -0
  198. package/dist/lib/std/templates/assured-deployment.yaml +45 -0
  199. package/dist/lib/std/templates/erc-2470.yaml +67 -0
  200. package/dist/lib/std/templates/min-balance.yaml +32 -0
  201. package/dist/lib/std/templates/nano-universal-deployer.yaml +59 -0
  202. package/dist/lib/std/templates/raw-erc-2470.yaml +59 -0
  203. package/dist/lib/std/templates/raw-nano-universal-deployer.yaml +51 -0
  204. package/dist/lib/std/templates/raw-sequence-universal-deployer-2.yaml +48 -0
  205. package/dist/lib/std/templates/sequence-universal-deployer-2.yaml +57 -0
  206. package/dist/lib/types/__tests__/json-request-action.spec.d.ts +2 -0
  207. package/dist/lib/types/__tests__/json-request-action.spec.d.ts.map +1 -0
  208. package/dist/lib/types/__tests__/json-request-action.spec.js +219 -0
  209. package/dist/lib/types/__tests__/json-request-action.spec.js.map +1 -0
  210. package/dist/lib/types/__tests__/read-json-value.spec.d.ts +2 -0
  211. package/dist/lib/types/__tests__/read-json-value.spec.d.ts.map +1 -0
  212. package/dist/lib/types/__tests__/read-json-value.spec.js +233 -0
  213. package/dist/lib/types/__tests__/read-json-value.spec.js.map +1 -0
  214. package/dist/lib/types/actions.d.ts +74 -0
  215. package/dist/lib/types/actions.d.ts.map +1 -0
  216. package/dist/lib/types/actions.js +18 -0
  217. package/dist/lib/types/actions.js.map +1 -0
  218. package/dist/lib/types/artifacts.d.ts +15 -0
  219. package/dist/lib/types/artifacts.d.ts.map +1 -0
  220. package/dist/lib/types/artifacts.js +3 -0
  221. package/dist/lib/types/artifacts.js.map +1 -0
  222. package/dist/lib/types/buildinfo.d.ts +112 -0
  223. package/dist/lib/types/buildinfo.d.ts.map +1 -0
  224. package/dist/lib/types/buildinfo.js +3 -0
  225. package/dist/lib/types/buildinfo.js.map +1 -0
  226. package/dist/lib/types/conditions.d.ts +17 -0
  227. package/dist/lib/types/conditions.d.ts.map +1 -0
  228. package/dist/lib/types/conditions.js +21 -0
  229. package/dist/lib/types/conditions.js.map +1 -0
  230. package/dist/lib/types/contracts.d.ts +14 -0
  231. package/dist/lib/types/contracts.d.ts.map +1 -0
  232. package/dist/lib/types/contracts.js +3 -0
  233. package/dist/lib/types/contracts.js.map +1 -0
  234. package/dist/lib/types/definitions.d.ts +51 -0
  235. package/dist/lib/types/definitions.d.ts.map +1 -0
  236. package/dist/lib/types/definitions.js +3 -0
  237. package/dist/lib/types/definitions.js.map +1 -0
  238. package/dist/lib/types/index.d.ts +9 -0
  239. package/dist/lib/types/index.d.ts.map +1 -0
  240. package/dist/lib/types/index.js +25 -0
  241. package/dist/lib/types/index.js.map +1 -0
  242. package/dist/lib/types/network.d.ts +9 -0
  243. package/dist/lib/types/network.d.ts.map +1 -0
  244. package/dist/lib/types/network.js +3 -0
  245. package/dist/lib/types/network.js.map +1 -0
  246. package/dist/lib/types/project.d.ts +5 -0
  247. package/dist/lib/types/project.d.ts.map +1 -0
  248. package/dist/lib/types/project.js +3 -0
  249. package/dist/lib/types/project.js.map +1 -0
  250. package/dist/lib/types/task.d.ts +9 -0
  251. package/dist/lib/types/task.d.ts.map +1 -0
  252. package/dist/lib/types/task.js +3 -0
  253. package/dist/lib/types/task.js.map +1 -0
  254. package/dist/lib/types/values.d.ts +78 -0
  255. package/dist/lib/types/values.d.ts.map +1 -0
  256. package/dist/lib/types/values.js +3 -0
  257. package/dist/lib/types/values.js.map +1 -0
  258. package/dist/lib/utils/validation.d.ts +5 -0
  259. package/dist/lib/utils/validation.d.ts.map +1 -0
  260. package/dist/lib/utils/validation.js +77 -0
  261. package/dist/lib/utils/validation.js.map +1 -0
  262. package/dist/lib/validation/contract-references.d.ts +12 -0
  263. package/dist/lib/validation/contract-references.d.ts.map +1 -0
  264. package/dist/lib/validation/contract-references.js +112 -0
  265. package/dist/lib/validation/contract-references.js.map +1 -0
  266. package/dist/lib/validation/index.d.ts +1 -0
  267. package/dist/lib/validation/index.d.ts.map +1 -0
  268. package/dist/lib/validation/index.js +2 -0
  269. package/dist/lib/validation/index.js.map +1 -0
  270. package/dist/lib/verification/__tests__/etherscan.spec.d.ts +2 -0
  271. package/dist/lib/verification/__tests__/etherscan.spec.d.ts.map +1 -0
  272. package/dist/lib/verification/__tests__/etherscan.spec.js +565 -0
  273. package/dist/lib/verification/__tests__/etherscan.spec.js.map +1 -0
  274. package/dist/lib/verification/__tests__/sourcify.spec.d.ts +2 -0
  275. package/dist/lib/verification/__tests__/sourcify.spec.d.ts.map +1 -0
  276. package/dist/lib/verification/__tests__/sourcify.spec.js +212 -0
  277. package/dist/lib/verification/__tests__/sourcify.spec.js.map +1 -0
  278. package/dist/lib/verification/etherscan.d.ts +56 -0
  279. package/dist/lib/verification/etherscan.d.ts.map +1 -0
  280. package/dist/lib/verification/etherscan.js +340 -0
  281. package/dist/lib/verification/etherscan.js.map +1 -0
  282. package/dist/lib/verification/sourcify.d.ts +12 -0
  283. package/dist/lib/verification/sourcify.d.ts.map +1 -0
  284. package/dist/lib/verification/sourcify.js +227 -0
  285. package/dist/lib/verification/sourcify.js.map +1 -0
  286. package/eslint.config.js +48 -0
  287. package/examples/jobs/guards-v1.yaml +17 -0
  288. package/examples/jobs/sequence-seq-0001-patch.yaml +59 -0
  289. package/examples/jobs/sequence-v1.yaml +59 -0
  290. package/examples/templates/sequence-factory-v1.yaml +56 -0
  291. package/jest.config.js +25 -0
  292. package/package.json +68 -0
  293. package/src/cli.ts +17 -0
  294. package/src/commands/common.ts +61 -0
  295. package/src/commands/dry.ts +208 -0
  296. package/src/commands/etherscan.ts +360 -0
  297. package/src/commands/index.ts +5 -0
  298. package/src/commands/list.ts +249 -0
  299. package/src/commands/run.ts +136 -0
  300. package/src/commands/utils.ts +52 -0
  301. package/src/index.ts +67 -0
  302. package/src/lib/__tests__/deployer-events.spec.ts +338 -0
  303. package/src/lib/__tests__/deployer.spec.ts +1204 -0
  304. package/src/lib/__tests__/network-utils.spec.ts +181 -0
  305. package/src/lib/artifacts/__tests__/fixtures/contract1.json +19 -0
  306. package/src/lib/artifacts/__tests__/fixtures/contract2.json +19 -0
  307. package/src/lib/artifacts/__tests__/fixtures/duplicate-name.json +19 -0
  308. package/src/lib/artifacts/__tests__/fixtures/nested/nested-contract.json +18 -0
  309. package/src/lib/artifacts/__tests__/fixtures/not-an-artifact.json +8 -0
  310. package/src/lib/artifacts/__tests__/fixtures/readme.txt +2 -0
  311. package/src/lib/contracts/__tests__/repository.spec.ts +344 -0
  312. package/src/lib/contracts/repository.ts +313 -0
  313. package/src/lib/core/__tests__/engine.spec.ts +1514 -0
  314. package/src/lib/core/__tests__/graph.spec.ts +125 -0
  315. package/src/lib/core/__tests__/json-integration.spec.ts +360 -0
  316. package/src/lib/core/__tests__/loader.spec.ts +334 -0
  317. package/src/lib/core/__tests__/multi-platform-verification.spec.ts +406 -0
  318. package/src/lib/core/__tests__/resolver.spec.ts +1693 -0
  319. package/src/lib/core/__tests__/static-action.spec.ts +172 -0
  320. package/src/lib/core/context.ts +127 -0
  321. package/src/lib/core/engine.ts +1531 -0
  322. package/src/lib/core/graph.ts +252 -0
  323. package/src/lib/core/loader.ts +263 -0
  324. package/src/lib/core/resolver.ts +498 -0
  325. package/src/lib/deployer.ts +768 -0
  326. package/src/lib/events/__tests__/event-system.spec.ts +343 -0
  327. package/src/lib/events/cli-adapter.ts +325 -0
  328. package/src/lib/events/emitter.ts +62 -0
  329. package/src/lib/events/index.ts +3 -0
  330. package/src/lib/events/types.ts +469 -0
  331. package/src/lib/index.ts +14 -0
  332. package/src/lib/network-loader.ts +59 -0
  333. package/src/lib/network-utils.ts +64 -0
  334. package/src/lib/parsers/__tests__/buildinfo.spec.ts +122 -0
  335. package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-bytecode-buildinfo.json +62 -0
  336. package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-json.txt +2 -0
  337. package/src/lib/parsers/__tests__/fixtures/buildinfo/multi-contract-buildinfo.json +89 -0
  338. package/src/lib/parsers/__tests__/fixtures/buildinfo/no-contracts-buildinfo.json +17 -0
  339. package/src/lib/parsers/__tests__/fixtures/buildinfo/simple-buildinfo.json +63 -0
  340. package/src/lib/parsers/__tests__/fixtures/buildinfo/wrong-format.json +4 -0
  341. package/src/lib/parsers/__tests__/job.spec.ts +335 -0
  342. package/src/lib/parsers/__tests__/template.spec.ts +111 -0
  343. package/src/lib/parsers/artifact/__tests__/artifact.spec.ts +117 -0
  344. package/src/lib/parsers/artifact/__tests__/fixtures/empty-bytecode.json +5 -0
  345. package/src/lib/parsers/artifact/__tests__/fixtures/hardhat-artifact.json +67 -0
  346. package/src/lib/parsers/artifact/__tests__/fixtures/invalid-bytecode.json +5 -0
  347. package/src/lib/parsers/artifact/__tests__/fixtures/invalid-json.txt +11 -0
  348. package/src/lib/parsers/artifact/__tests__/fixtures/minimal-artifact.json +5 -0
  349. package/src/lib/parsers/artifact/__tests__/fixtures/missing-abi.json +4 -0
  350. package/src/lib/parsers/artifact/__tests__/fixtures/missing-bytecode.json +11 -0
  351. package/src/lib/parsers/artifact/__tests__/fixtures/missing-contract-name.json +11 -0
  352. package/src/lib/parsers/artifact/__tests__/fixtures/simple-artifact.json +40 -0
  353. package/src/lib/parsers/artifact/__tests__/fixtures/wrong-types.json +7 -0
  354. package/src/lib/parsers/artifact/foundry-1.2.ts +72 -0
  355. package/src/lib/parsers/artifact/index.ts +27 -0
  356. package/src/lib/parsers/artifact/types.ts +9 -0
  357. package/src/lib/parsers/buildinfo.ts +127 -0
  358. package/src/lib/parsers/constants.ts +56 -0
  359. package/src/lib/parsers/index.ts +5 -0
  360. package/src/lib/parsers/job.ts +101 -0
  361. package/src/lib/parsers/template.ts +131 -0
  362. package/src/lib/std/templates/assured-deployment.yaml +45 -0
  363. package/src/lib/std/templates/erc-2470.yaml +67 -0
  364. package/src/lib/std/templates/min-balance.yaml +32 -0
  365. package/src/lib/std/templates/nano-universal-deployer.yaml +59 -0
  366. package/src/lib/std/templates/raw-erc-2470.yaml +59 -0
  367. package/src/lib/std/templates/raw-nano-universal-deployer.yaml +51 -0
  368. package/src/lib/std/templates/raw-sequence-universal-deployer-2.yaml +48 -0
  369. package/src/lib/std/templates/sequence-universal-deployer-2.yaml +57 -0
  370. package/src/lib/types/__tests__/json-request-action.spec.ts +243 -0
  371. package/src/lib/types/__tests__/read-json-value.spec.ts +264 -0
  372. package/src/lib/types/actions.ts +127 -0
  373. package/src/lib/types/artifacts.ts +21 -0
  374. package/src/lib/types/buildinfo.ts +116 -0
  375. package/src/lib/types/conditions.ts +50 -0
  376. package/src/lib/types/contracts.ts +23 -0
  377. package/src/lib/types/definitions.ts +68 -0
  378. package/src/lib/types/index.ts +8 -0
  379. package/src/lib/types/network.ts +22 -0
  380. package/src/lib/types/project.ts +9 -0
  381. package/src/lib/types/task.ts +9 -0
  382. package/src/lib/types/values.ts +116 -0
  383. package/src/lib/utils/validation.ts +116 -0
  384. package/src/lib/validation/contract-references.ts +210 -0
  385. package/src/lib/validation/index.ts +1 -0
  386. package/src/lib/verification/__tests__/etherscan.spec.ts +710 -0
  387. package/src/lib/verification/__tests__/sourcify.spec.ts +288 -0
  388. package/src/lib/verification/etherscan.ts +546 -0
  389. package/src/lib/verification/sourcify.ts +248 -0
  390. package/test_validation/artifacts/TestContract.json +9 -0
  391. package/test_validation/jobs/test-missing.yaml +16 -0
  392. package/test_validation/networks.yaml +3 -0
  393. package/tsconfig.json +36 -0
@@ -0,0 +1,1212 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const ethers_1 = require("ethers");
4
+ const engine_1 = require("../engine");
5
+ const context_1 = require("../context");
6
+ const repository_1 = require("../../contracts/repository");
7
+ const etherscan_1 = require("../../verification/etherscan");
8
+ const TEST_ADDRESSES = {
9
+ DEPLOYER: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
10
+ RECIPIENT_1: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
11
+ RECIPIENT_2: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC',
12
+ RECIPIENT_3: '0x90F79bf6EB2c4f870365E785982E1f101E93b906',
13
+ RECIPIENT_4: '0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65',
14
+ RECIPIENT_5: '0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc',
15
+ CONTRACT_ADDRESS: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
16
+ DUMMY_ADDRESS: '0x1234567890123456789012345678901234567890'
17
+ };
18
+ const TEST_BYTECODES = {
19
+ SIMPLE_CONTRACT: '0x6080604052348015600e575f5ffd5b5060c180601a5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c806390c52443146034578063d09de08a14604d575b5f5ffd5b603b5f5481565b60405190815260200160405180910390f35b60536055565b005b5f805490806061836068565b9190505550565b5f60018201608457634e487b7160e01b5f52601160045260245ffd5b506001019056fea264697066735822122061c8cc43c72d6b23b16f7a7337dd15b93d71eb94a9d5247911e39f486e1f94f964736f6c634300081e0033',
20
+ SIMPLE_RETURN_42: '0x6080604052348015600f57600080fd5b5060b68061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063a87d942c14602d575b600080fd5b60336035565b005b6000602a9050909156fea2646970667358221220d1b0e2d6c9f3e8a6f5b8d2e3a4c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c556',
21
+ MINI_CONTRACT: '0x608060405234801561000f575f5ffd5b5060043610610034575f3560e01c80636df5b97a14610038578063f8a8fd6d14610068575b5f5ffd5b610052600480360381019061004d91906100da565b610086565b60405161005f9190610127565b60405180910390f35b61007061009b565b60405161007d9190610127565b60405180910390f35b5f8183610093919061016d565b905092915050565b5f602a905090565b5f5ffd5b5f819050919050565b6100b9816100a7565b81146100c3575f5ffd5b50565b5f813590506100d4816100b0565b92915050565b5f5f604083850312156100f0576100ef6100a3565b5b5f6100fd858286016100c6565b925050602061010e858286016100c6565b9150509250929050565b610121816100a7565b82525050565b5f60208201905061013a5f830184610118565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610177826100a7565b9150610182836100a7565b9250828202610190816100a7565b915082820484148315176101a7576101a6610140565b5b509291505056fea264697066735822122071d40daa3d2beacd91f29d29ccf1c0b6f312e805f50b37166267c0a2a55e6e6164736f6c634300081c0033',
22
+ MINIMAL_DEPLOY: '0x608060405234801561000f575f5ffd5b50603e80601c5f395ff3fe60806040525f80fdfea264697066735822122071d40daa3d2beacd91f29d29ccf1c0b6f312e805f50b37166267c0a2a55e6e6164736f6c634300081c0033'
23
+ };
24
+ const TEST_VALUES = {
25
+ ONE_ETH: '1000000000000000000',
26
+ HALF_ETH: '500000000000000000',
27
+ TWO_ETH: '2000000000000000000',
28
+ SMALL_AMOUNT: '1000000000000000000'
29
+ };
30
+ const TEST_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
31
+ describe('ExecutionEngine', () => {
32
+ let engine;
33
+ let context;
34
+ let mockNetwork;
35
+ let mockRegistry;
36
+ let templates;
37
+ let anvilProvider;
38
+ beforeAll(async () => {
39
+ const rpcUrl = process.env.RPC_URL || 'http://127.0.0.1:8545';
40
+ mockNetwork = { name: 'testnet', chainId: 999, rpcUrl };
41
+ const provider = new ethers_1.ethers.JsonRpcProvider(rpcUrl);
42
+ await provider.getNetwork();
43
+ });
44
+ beforeEach(async () => {
45
+ const rpcUrl = process.env.RPC_URL || 'http://127.0.0.1:8545';
46
+ anvilProvider = new ethers_1.ethers.JsonRpcProvider(rpcUrl);
47
+ mockRegistry = new repository_1.ContractRepository();
48
+ context = new context_1.ExecutionContext(mockNetwork, TEST_PRIVATE_KEY, mockRegistry);
49
+ templates = new Map();
50
+ const verificationRegistry = new etherscan_1.VerificationPlatformRegistry();
51
+ engine = new engine_1.ExecutionEngine(templates, undefined, verificationRegistry);
52
+ });
53
+ afterEach(async () => {
54
+ if (anvilProvider) {
55
+ try {
56
+ if (anvilProvider.destroy) {
57
+ await anvilProvider.destroy();
58
+ }
59
+ }
60
+ catch (error) {
61
+ }
62
+ }
63
+ if (context) {
64
+ try {
65
+ await context.dispose();
66
+ }
67
+ catch (error) {
68
+ }
69
+ }
70
+ });
71
+ describe('executeJob', () => {
72
+ it('should execute a simple job with no dependencies', async () => {
73
+ const job = {
74
+ name: 'simple-job',
75
+ version: '1.0.0',
76
+ actions: [
77
+ {
78
+ name: 'send-eth',
79
+ template: 'send-transaction',
80
+ arguments: {
81
+ to: TEST_ADDRESSES.RECIPIENT_1,
82
+ value: TEST_VALUES.ONE_ETH,
83
+ data: '0x'
84
+ }
85
+ }
86
+ ]
87
+ };
88
+ await expect(engine.executeJob(job, context)).resolves.not.toThrow();
89
+ expect(context.getOutput('send-eth.hash')).toBeDefined();
90
+ expect(context.getOutput('send-eth.receipt')).toBeDefined();
91
+ });
92
+ it('should execute actions in dependency order', async () => {
93
+ const executionOrder = [];
94
+ const originalExecuteAction = engine.executeAction;
95
+ engine.executeAction = async function (action, ctx, scope) {
96
+ const actionName = 'name' in action ? action.name : action.type;
97
+ if (actionName) {
98
+ executionOrder.push(actionName);
99
+ }
100
+ };
101
+ const job = {
102
+ name: 'dependency-job',
103
+ version: '1.0.0',
104
+ actions: [
105
+ {
106
+ name: 'action-c',
107
+ template: 'send-transaction',
108
+ arguments: { to: TEST_ADDRESSES.DUMMY_ADDRESS, data: '0x' },
109
+ depends_on: ['action-a', 'action-b']
110
+ },
111
+ {
112
+ name: 'action-a',
113
+ template: 'send-transaction',
114
+ arguments: { to: TEST_ADDRESSES.DUMMY_ADDRESS, data: '0x' }
115
+ },
116
+ {
117
+ name: 'action-b',
118
+ template: 'send-transaction',
119
+ arguments: { to: TEST_ADDRESSES.DUMMY_ADDRESS, data: '0x' },
120
+ depends_on: ['action-a']
121
+ }
122
+ ]
123
+ };
124
+ await engine.executeJob(job, context);
125
+ engine.executeAction = originalExecuteAction;
126
+ expect(executionOrder).toEqual(['action-a', 'action-b', 'action-c']);
127
+ });
128
+ it('should throw on circular dependencies within a job', async () => {
129
+ const job = {
130
+ name: 'circular-job',
131
+ version: '1.0.0',
132
+ actions: [
133
+ {
134
+ name: 'action-a',
135
+ template: 'send-transaction',
136
+ arguments: { to: TEST_ADDRESSES.DUMMY_ADDRESS, data: '0x' },
137
+ depends_on: ['action-b']
138
+ },
139
+ {
140
+ name: 'action-b',
141
+ template: 'send-transaction',
142
+ arguments: { to: TEST_ADDRESSES.DUMMY_ADDRESS, data: '0x' },
143
+ depends_on: ['action-a']
144
+ }
145
+ ]
146
+ };
147
+ await expect(engine.executeJob(job, context)).rejects.toThrow('Circular dependency detected among actions in job "circular-job".');
148
+ });
149
+ it('should throw on invalid dependencies within a job', async () => {
150
+ const job = {
151
+ name: 'invalid-dep-job',
152
+ version: '1.0.0',
153
+ actions: [
154
+ {
155
+ name: 'action-a',
156
+ template: 'send-transaction',
157
+ arguments: { to: TEST_ADDRESSES.DUMMY_ADDRESS, data: '0x' },
158
+ depends_on: ['non-existent-action']
159
+ }
160
+ ]
161
+ };
162
+ await expect(engine.executeJob(job, context)).rejects.toThrow('Action "action-a" in job "invalid-dep-job" has an invalid dependency on "non-existent-action", which does not exist.');
163
+ });
164
+ });
165
+ describe('executeAction', () => {
166
+ it('should skip action when skip condition is met', async () => {
167
+ context.setOutput('should_skip', 1);
168
+ const action = {
169
+ name: 'skipped-action',
170
+ template: 'send-transaction',
171
+ arguments: { to: TEST_ADDRESSES.DUMMY_ADDRESS, data: '0x' },
172
+ skip_condition: [{ type: 'basic-arithmetic', arguments: { operation: 'eq', values: ['{{should_skip}}', 1] } }]
173
+ };
174
+ await engine.executeAction(action, context, new Map());
175
+ expect(() => context.getOutput('skipped-action.hash')).toThrow();
176
+ });
177
+ it('should execute action when skip condition is not met', async () => {
178
+ context.setOutput('should_skip', 0);
179
+ const action = {
180
+ name: 'executed-action',
181
+ template: 'send-transaction',
182
+ arguments: {
183
+ to: TEST_ADDRESSES.RECIPIENT_1,
184
+ value: TEST_VALUES.ONE_ETH,
185
+ data: '0x'
186
+ },
187
+ skip_condition: [{ type: 'basic-arithmetic', arguments: { operation: 'eq', values: ['{{should_skip}}', 1] } }]
188
+ };
189
+ await engine.executeAction(action, context, new Map());
190
+ expect(context.getOutput('executed-action.hash')).toBeDefined();
191
+ });
192
+ it('should call executeTemplate for template actions', async () => {
193
+ const template = {
194
+ name: 'test-template',
195
+ actions: [
196
+ {
197
+ type: 'send-transaction',
198
+ name: 'param-action',
199
+ arguments: {
200
+ to: TEST_ADDRESSES.RECIPIENT_1,
201
+ value: TEST_VALUES.ONE_ETH,
202
+ data: '0x'
203
+ }
204
+ }
205
+ ]
206
+ };
207
+ templates.set('test-template', template);
208
+ const action = {
209
+ name: 'template-action',
210
+ template: 'test-template',
211
+ arguments: {}
212
+ };
213
+ await engine.executeAction(action, context, new Map());
214
+ });
215
+ it('should call executePrimitive for primitive actions', async () => {
216
+ const action = {
217
+ type: 'send-transaction',
218
+ name: 'primitive-action',
219
+ arguments: {
220
+ to: TEST_ADDRESSES.RECIPIENT_1,
221
+ value: TEST_VALUES.ONE_ETH,
222
+ data: '0x'
223
+ }
224
+ };
225
+ await engine.executeAction(action, context, new Map());
226
+ expect(context.getOutput('primitive-action.hash')).toBeDefined();
227
+ });
228
+ });
229
+ describe('executeTemplate', () => {
230
+ it('should execute template with setup block', async () => {
231
+ const executedActions = [];
232
+ const originalExecuteAction = engine.executeAction;
233
+ engine.executeAction = async function (action, ctx, scope) {
234
+ const actionName = 'name' in action ? action.name : action.type;
235
+ if (actionName) {
236
+ executedActions.push(actionName);
237
+ if (actionName === 'setup-action') {
238
+ ctx.setOutput('setup-action.hash', 'mock-setup-hash');
239
+ ctx.setOutput('setup-action.receipt', { status: 1, blockNumber: 100 });
240
+ }
241
+ else if (actionName === 'main-action') {
242
+ ctx.setOutput('main-action.hash', 'mock-main-hash');
243
+ ctx.setOutput('main-action.receipt', { status: 1, blockNumber: 101 });
244
+ }
245
+ }
246
+ };
247
+ const template = {
248
+ name: 'template-with-setup',
249
+ setup: {
250
+ actions: [
251
+ {
252
+ type: 'send-transaction',
253
+ name: 'setup-action',
254
+ arguments: {
255
+ to: TEST_ADDRESSES.RECIPIENT_2,
256
+ value: TEST_VALUES.HALF_ETH,
257
+ data: '0x'
258
+ }
259
+ }
260
+ ]
261
+ },
262
+ actions: [
263
+ {
264
+ type: 'send-transaction',
265
+ name: 'main-action',
266
+ arguments: {
267
+ to: TEST_ADDRESSES.RECIPIENT_3,
268
+ value: TEST_VALUES.ONE_ETH,
269
+ data: '0x'
270
+ }
271
+ }
272
+ ]
273
+ };
274
+ templates.set('template-with-setup', template);
275
+ const callingAction = {
276
+ name: 'test-call',
277
+ template: 'template-with-setup',
278
+ arguments: {}
279
+ };
280
+ await engine.executeTemplate(callingAction, 'template-with-setup', context);
281
+ engine.executeAction = originalExecuteAction;
282
+ expect(executedActions).toEqual(['setup-action', 'main-action']);
283
+ expect(context.getOutput('setup-action.hash')).toBeDefined();
284
+ expect(context.getOutput('main-action.hash')).toBeDefined();
285
+ });
286
+ it('should skip template actions when template skip condition is met', async () => {
287
+ context.setOutput('skip_template', 1);
288
+ const template = {
289
+ name: 'skippable-template',
290
+ skip_condition: [{ type: 'basic-arithmetic', arguments: { operation: 'eq', values: ['{{skip_template}}', 1] } }],
291
+ actions: [
292
+ {
293
+ type: 'send-transaction',
294
+ name: 'skipped-main-action',
295
+ arguments: {
296
+ to: TEST_ADDRESSES.RECIPIENT_1,
297
+ value: TEST_VALUES.ONE_ETH,
298
+ data: '0x'
299
+ }
300
+ }
301
+ ]
302
+ };
303
+ templates.set('skippable-template', template);
304
+ const callingAction = {
305
+ name: 'test-call',
306
+ template: 'skippable-template',
307
+ arguments: {}
308
+ };
309
+ await engine.executeTemplate(callingAction, 'skippable-template', context);
310
+ expect(() => context.getOutput('skipped-main-action.hash')).toThrow();
311
+ });
312
+ it('should skip template actions when setup skip condition is met', async () => {
313
+ context.setOutput('skip_setup', 1);
314
+ const template = {
315
+ name: 'skippable-setup-template',
316
+ setup: {
317
+ skip_condition: [{ type: 'basic-arithmetic', arguments: { operation: 'eq', values: ['{{skip_setup}}', 1] } }],
318
+ actions: [
319
+ {
320
+ type: 'send-transaction',
321
+ name: 'setup-action',
322
+ arguments: {
323
+ to: TEST_ADDRESSES.RECIPIENT_4,
324
+ value: TEST_VALUES.ONE_ETH,
325
+ data: '0x'
326
+ }
327
+ }
328
+ ]
329
+ },
330
+ actions: [
331
+ {
332
+ type: 'send-transaction',
333
+ name: 'main-action-after-skipped-setup',
334
+ arguments: {
335
+ to: TEST_ADDRESSES.RECIPIENT_1,
336
+ value: TEST_VALUES.ONE_ETH,
337
+ data: '0x'
338
+ }
339
+ }
340
+ ]
341
+ };
342
+ templates.set('skippable-setup-template', template);
343
+ const callingAction = {
344
+ name: 'test-call',
345
+ template: 'skippable-setup-template',
346
+ arguments: {}
347
+ };
348
+ await engine.executeTemplate(callingAction, 'skippable-setup-template', context);
349
+ expect(() => context.getOutput('setup-action.hash')).toThrow();
350
+ expect(context.getOutput('main-action-after-skipped-setup.hash')).toBeDefined();
351
+ });
352
+ it('should pass arguments to template and resolve outputs', async () => {
353
+ const template = {
354
+ name: 'parameterized-template',
355
+ actions: [
356
+ {
357
+ type: 'send-transaction',
358
+ name: 'param-action',
359
+ arguments: {
360
+ to: '{{target_address}}',
361
+ value: '{{amount}}',
362
+ data: '0x'
363
+ }
364
+ }
365
+ ],
366
+ outputs: {
367
+ transaction_hash: '{{param-action.hash}}',
368
+ doubled_amount: { type: 'basic-arithmetic', arguments: { operation: 'mul', values: ['{{amount}}', 2] } }
369
+ }
370
+ };
371
+ templates.set('parameterized-template', template);
372
+ const callingAction = {
373
+ name: 'test-param-call',
374
+ template: 'parameterized-template',
375
+ arguments: {
376
+ target_address: TEST_ADDRESSES.RECIPIENT_1,
377
+ amount: TEST_VALUES.ONE_ETH
378
+ }
379
+ };
380
+ await engine.executeTemplate(callingAction, 'parameterized-template', context);
381
+ expect(context.getOutput('test-param-call.transaction_hash')).toBeDefined();
382
+ expect(context.getOutput('test-param-call.doubled_amount')).toBe('2000000000000000000');
383
+ });
384
+ it('should allow job action custom output map to override template outputs', async () => {
385
+ const originalExecuteAction = engine.executeAction;
386
+ engine.executeAction = async function (action, ctx, scope) {
387
+ const actionName = 'name' in action ? action.name : action.type;
388
+ if (actionName === 'param-action') {
389
+ ctx.setOutput('param-action.hash', '0xhash123');
390
+ ctx.setOutput('param-action.receipt', { status: 1, blockNumber: 111 });
391
+ }
392
+ };
393
+ const template = {
394
+ name: 'tpl-custom-output',
395
+ actions: [
396
+ {
397
+ type: 'send-transaction',
398
+ name: 'param-action',
399
+ arguments: {
400
+ to: TEST_ADDRESSES.RECIPIENT_1,
401
+ value: TEST_VALUES.ONE_ETH,
402
+ data: '0x'
403
+ }
404
+ }
405
+ ],
406
+ outputs: {
407
+ transaction_hash: '{{param-action.hash}}',
408
+ receipt_block: '{{param-action.receipt.blockNumber}}'
409
+ }
410
+ };
411
+ templates.set('tpl-custom-output', template);
412
+ const callingAction = {
413
+ name: 'custom-call',
414
+ template: 'tpl-custom-output',
415
+ arguments: {},
416
+ output: {
417
+ myHash: '{{param-action.hash}}',
418
+ staticValue: '42'
419
+ }
420
+ };
421
+ await engine.executeTemplate(callingAction, 'tpl-custom-output', context);
422
+ engine.executeAction = originalExecuteAction;
423
+ expect(context.getOutput('custom-call.myHash')).toBe('0xhash123');
424
+ expect(context.getOutput('custom-call.staticValue')).toBe('42');
425
+ expect(() => context.getOutput('custom-call.transaction_hash')).toThrow();
426
+ expect(() => context.getOutput('custom-call.receipt_block')).toThrow();
427
+ });
428
+ it('should throw when template is not found', async () => {
429
+ const callingAction = {
430
+ name: 'test-call',
431
+ template: 'non-existent-template',
432
+ arguments: {}
433
+ };
434
+ await expect(engine.executeTemplate(callingAction, 'non-existent-template', context))
435
+ .rejects.toThrow('Template "non-existent-template" not found');
436
+ });
437
+ });
438
+ describe('executePrimitive', () => {
439
+ describe('send-transaction', () => {
440
+ it('should send a transaction successfully', async () => {
441
+ const action = {
442
+ type: 'send-transaction',
443
+ name: 'test-tx',
444
+ arguments: {
445
+ to: TEST_ADDRESSES.RECIPIENT_1,
446
+ value: TEST_VALUES.ONE_ETH,
447
+ data: '0x'
448
+ }
449
+ };
450
+ await engine.executePrimitive(action, context, new Map());
451
+ const hash = context.getOutput('test-tx.hash');
452
+ const receipt = context.getOutput('test-tx.receipt');
453
+ expect(hash).toBeDefined();
454
+ expect(receipt).toBeDefined();
455
+ expect(receipt.status).toBe(1);
456
+ });
457
+ it('should send transaction with resolved arguments', async () => {
458
+ context.setOutput('recipient', TEST_ADDRESSES.RECIPIENT_1);
459
+ context.setOutput('amount', TEST_VALUES.ONE_ETH);
460
+ const action = {
461
+ type: 'send-transaction',
462
+ name: 'resolved-tx',
463
+ arguments: {
464
+ to: '{{recipient}}',
465
+ value: '{{amount}}',
466
+ data: '0x1234'
467
+ }
468
+ };
469
+ await engine.executePrimitive(action, context, new Map());
470
+ expect(context.getOutput('resolved-tx.hash')).toBeDefined();
471
+ });
472
+ it('should handle transaction without value and data', async () => {
473
+ const action = {
474
+ type: 'send-transaction',
475
+ name: 'minimal-tx',
476
+ arguments: {
477
+ to: TEST_ADDRESSES.RECIPIENT_1
478
+ }
479
+ };
480
+ await engine.executePrimitive(action, context, new Map());
481
+ expect(context.getOutput('minimal-tx.hash')).toBeDefined();
482
+ });
483
+ it('should not store outputs when action has no name', async () => {
484
+ const action = {
485
+ type: 'send-transaction',
486
+ arguments: {
487
+ to: TEST_ADDRESSES.RECIPIENT_1,
488
+ value: TEST_VALUES.ONE_ETH,
489
+ data: '0x'
490
+ }
491
+ };
492
+ await engine.executePrimitive(action, context, new Map());
493
+ const outputs = context.outputs;
494
+ expect(outputs.size).toBe(0);
495
+ });
496
+ it('should throw on invalid address', async () => {
497
+ const action = {
498
+ type: 'send-transaction',
499
+ arguments: {
500
+ to: 'invalid-address',
501
+ value: '1000000000000000000',
502
+ data: '0x'
503
+ }
504
+ };
505
+ await expect(engine.executePrimitive(action, context, new Map()))
506
+ .rejects.toThrow();
507
+ });
508
+ it('should apply gas multiplier when network gasLimit is set', async () => {
509
+ const mockNetwork = { gasLimit: 100000 };
510
+ jest.spyOn(context, 'getNetwork').mockReturnValue(mockNetwork);
511
+ const action = {
512
+ type: 'send-transaction',
513
+ name: 'gas-multiplier-tx',
514
+ arguments: {
515
+ to: TEST_ADDRESSES.RECIPIENT_1,
516
+ value: TEST_VALUES.ONE_ETH,
517
+ gasMultiplier: 1.5
518
+ }
519
+ };
520
+ const mockSendTransaction = jest.fn().mockResolvedValue({
521
+ hash: '0x123',
522
+ wait: jest.fn().mockResolvedValue({ status: 1, blockNumber: 123 })
523
+ });
524
+ const resolvedSigner = await context.getResolvedSigner();
525
+ jest.spyOn(resolvedSigner, 'sendTransaction').mockImplementation(mockSendTransaction);
526
+ await engine.executePrimitive(action, context, new Map());
527
+ expect(mockSendTransaction).toHaveBeenCalledWith(expect.objectContaining({
528
+ gasLimit: 150000
529
+ }));
530
+ });
531
+ it('should estimate gas and apply multiplier when no network gasLimit is set', async () => {
532
+ const mockNetwork = {};
533
+ jest.spyOn(context, 'getNetwork').mockReturnValue(mockNetwork);
534
+ const resolvedSigner = await context.getResolvedSigner();
535
+ const mockEstimateGas = jest.fn().mockResolvedValue(BigInt(80000));
536
+ jest.spyOn(resolvedSigner, 'estimateGas').mockImplementation(mockEstimateGas);
537
+ const mockSendTransaction = jest.fn().mockResolvedValue({
538
+ hash: '0x123',
539
+ wait: jest.fn().mockResolvedValue({ status: 1, blockNumber: 123 })
540
+ });
541
+ jest.spyOn(resolvedSigner, 'sendTransaction').mockImplementation(mockSendTransaction);
542
+ const action = {
543
+ type: 'send-transaction',
544
+ name: 'gas-estimate-tx',
545
+ arguments: {
546
+ to: TEST_ADDRESSES.RECIPIENT_1,
547
+ gasMultiplier: 2.0
548
+ }
549
+ };
550
+ await engine.executePrimitive(action, context, new Map());
551
+ expect(mockEstimateGas).toHaveBeenCalledWith(expect.objectContaining({
552
+ to: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'
553
+ }));
554
+ expect(mockSendTransaction).toHaveBeenCalledWith(expect.objectContaining({
555
+ gasLimit: 160000
556
+ }));
557
+ });
558
+ it('should work with resolved gasMultiplier value', async () => {
559
+ context.setOutput('multiplier', 1.25);
560
+ const mockNetwork = { gasLimit: 100000 };
561
+ jest.spyOn(context, 'getNetwork').mockReturnValue(mockNetwork);
562
+ const mockSendTransaction = jest.fn().mockResolvedValue({
563
+ hash: '0x123',
564
+ wait: jest.fn().mockResolvedValue({ status: 1, blockNumber: 123 })
565
+ });
566
+ const resolvedSigner = await context.getResolvedSigner();
567
+ jest.spyOn(resolvedSigner, 'sendTransaction').mockImplementation(mockSendTransaction);
568
+ const action = {
569
+ type: 'send-transaction',
570
+ name: 'resolved-multiplier-tx',
571
+ arguments: {
572
+ to: TEST_ADDRESSES.RECIPIENT_1,
573
+ gasMultiplier: '{{multiplier}}'
574
+ }
575
+ };
576
+ await engine.executePrimitive(action, context, new Map());
577
+ expect(mockSendTransaction).toHaveBeenCalledWith(expect.objectContaining({
578
+ gasLimit: 125000
579
+ }));
580
+ });
581
+ it('should throw error for invalid gasMultiplier', async () => {
582
+ const action = {
583
+ type: 'send-transaction',
584
+ arguments: {
585
+ to: TEST_ADDRESSES.RECIPIENT_1,
586
+ gasMultiplier: -1.0
587
+ }
588
+ };
589
+ await expect(engine.executePrimitive(action, context, new Map()))
590
+ .rejects.toThrow('gasMultiplier must be a positive number');
591
+ });
592
+ it('should throw error for zero gasMultiplier', async () => {
593
+ const action = {
594
+ type: 'send-transaction',
595
+ arguments: {
596
+ to: TEST_ADDRESSES.RECIPIENT_1,
597
+ gasMultiplier: 0
598
+ }
599
+ };
600
+ await expect(engine.executePrimitive(action, context, new Map()))
601
+ .rejects.toThrow('gasMultiplier must be a positive number');
602
+ });
603
+ });
604
+ describe('create-contract', () => {
605
+ it('should create a contract successfully', async () => {
606
+ const action = {
607
+ type: 'create-contract',
608
+ name: 'test-contract',
609
+ arguments: {
610
+ data: TEST_BYTECODES.SIMPLE_CONTRACT
611
+ }
612
+ };
613
+ await engine.executePrimitive(action, context, new Map());
614
+ const hash = context.getOutput('test-contract.hash');
615
+ const receipt = context.getOutput('test-contract.receipt');
616
+ const address = context.getOutput('test-contract.address');
617
+ expect(hash).toBeDefined();
618
+ expect(receipt).toBeDefined();
619
+ expect(address).toBeDefined();
620
+ expect(receipt.status).toBe(1);
621
+ expect(receipt.contractAddress).toBe(address);
622
+ });
623
+ it('should create contract with resolved arguments', async () => {
624
+ context.setOutput('contract_bytecode', TEST_BYTECODES.SIMPLE_CONTRACT);
625
+ const action = {
626
+ type: 'create-contract',
627
+ name: 'resolved-contract',
628
+ arguments: {
629
+ data: '{{contract_bytecode}}'
630
+ }
631
+ };
632
+ await engine.executePrimitive(action, context, new Map());
633
+ expect(context.getOutput('resolved-contract.address')).toBeDefined();
634
+ });
635
+ it('should apply gas multiplier when network gasLimit is set', async () => {
636
+ const mockNetwork = { gasLimit: 200000 };
637
+ jest.spyOn(context, 'getNetwork').mockReturnValue(mockNetwork);
638
+ const action = {
639
+ type: 'create-contract',
640
+ name: 'gas-multiplier-contract',
641
+ arguments: {
642
+ data: TEST_BYTECODES.SIMPLE_CONTRACT,
643
+ gasMultiplier: 1.5
644
+ }
645
+ };
646
+ const mockSendTransaction = jest.fn().mockResolvedValue({
647
+ hash: '0x123',
648
+ wait: jest.fn().mockResolvedValue({
649
+ status: 1,
650
+ blockNumber: 123,
651
+ contractAddress: '0x5FbDB2315678afecb367f032d93F642f64180aa3'
652
+ })
653
+ });
654
+ const resolvedSigner = await context.getResolvedSigner();
655
+ jest.spyOn(resolvedSigner, 'sendTransaction').mockImplementation(mockSendTransaction);
656
+ await engine.executePrimitive(action, context, new Map());
657
+ expect(mockSendTransaction).toHaveBeenCalledWith(expect.objectContaining({
658
+ to: null,
659
+ gasLimit: 300000
660
+ }));
661
+ });
662
+ it('should estimate gas and apply multiplier when no network gasLimit is set', async () => {
663
+ const mockNetwork = {};
664
+ jest.spyOn(context, 'getNetwork').mockReturnValue(mockNetwork);
665
+ const resolvedSigner = await context.getResolvedSigner();
666
+ const mockEstimateGas = jest.fn().mockResolvedValue(BigInt(150000));
667
+ jest.spyOn(resolvedSigner, 'estimateGas').mockImplementation(mockEstimateGas);
668
+ const mockSendTransaction = jest.fn().mockResolvedValue({
669
+ hash: '0x123',
670
+ wait: jest.fn().mockResolvedValue({
671
+ status: 1,
672
+ blockNumber: 123,
673
+ contractAddress: '0x5FbDB2315678afecb367f032d93F642f64180aa3'
674
+ })
675
+ });
676
+ jest.spyOn(resolvedSigner, 'sendTransaction').mockImplementation(mockSendTransaction);
677
+ const action = {
678
+ type: 'create-contract',
679
+ name: 'gas-estimate-contract',
680
+ arguments: {
681
+ data: TEST_BYTECODES.SIMPLE_CONTRACT,
682
+ gasMultiplier: 2.0
683
+ }
684
+ };
685
+ await engine.executePrimitive(action, context, new Map());
686
+ expect(mockEstimateGas).toHaveBeenCalledWith(expect.objectContaining({
687
+ to: null,
688
+ data: TEST_BYTECODES.SIMPLE_CONTRACT
689
+ }));
690
+ expect(mockSendTransaction).toHaveBeenCalledWith(expect.objectContaining({
691
+ gasLimit: 300000
692
+ }));
693
+ });
694
+ it('should work with resolved gasMultiplier value', async () => {
695
+ context.setOutput('multiplier', 1.25);
696
+ const mockNetwork = { gasLimit: 200000 };
697
+ jest.spyOn(context, 'getNetwork').mockReturnValue(mockNetwork);
698
+ const mockSendTransaction = jest.fn().mockResolvedValue({
699
+ hash: '0x123',
700
+ wait: jest.fn().mockResolvedValue({
701
+ status: 1,
702
+ blockNumber: 123,
703
+ contractAddress: '0x5FbDB2315678afecb367f032d93F642f64180aa3'
704
+ })
705
+ });
706
+ const resolvedSigner = await context.getResolvedSigner();
707
+ jest.spyOn(resolvedSigner, 'sendTransaction').mockImplementation(mockSendTransaction);
708
+ const action = {
709
+ type: 'create-contract',
710
+ name: 'resolved-multiplier-contract',
711
+ arguments: {
712
+ data: TEST_BYTECODES.SIMPLE_CONTRACT,
713
+ gasMultiplier: '{{multiplier}}'
714
+ }
715
+ };
716
+ await engine.executePrimitive(action, context, new Map());
717
+ expect(mockSendTransaction).toHaveBeenCalledWith(expect.objectContaining({
718
+ gasLimit: 250000
719
+ }));
720
+ });
721
+ it('should throw error for invalid gasMultiplier', async () => {
722
+ const action = {
723
+ type: 'create-contract',
724
+ arguments: {
725
+ data: TEST_BYTECODES.SIMPLE_CONTRACT,
726
+ gasMultiplier: -1.0
727
+ }
728
+ };
729
+ await expect(engine.executePrimitive(action, context, new Map()))
730
+ .rejects.toThrow('gasMultiplier must be a positive number');
731
+ });
732
+ it('should throw error for zero gasMultiplier', async () => {
733
+ const action = {
734
+ type: 'create-contract',
735
+ arguments: {
736
+ data: TEST_BYTECODES.SIMPLE_CONTRACT,
737
+ gasMultiplier: 0
738
+ }
739
+ };
740
+ await expect(engine.executePrimitive(action, context, new Map()))
741
+ .rejects.toThrow('gasMultiplier must be a positive number');
742
+ });
743
+ it('should handle contract creation without value and gasMultiplier', async () => {
744
+ const action = {
745
+ type: 'create-contract',
746
+ name: 'minimal-contract',
747
+ arguments: {
748
+ data: TEST_BYTECODES.SIMPLE_CONTRACT
749
+ }
750
+ };
751
+ await engine.executePrimitive(action, context, new Map());
752
+ expect(context.getOutput('minimal-contract.address')).toBeDefined();
753
+ });
754
+ it('should not store outputs when action has no name', async () => {
755
+ const action = {
756
+ type: 'create-contract',
757
+ arguments: {
758
+ data: TEST_BYTECODES.SIMPLE_CONTRACT
759
+ }
760
+ };
761
+ const outputsBefore = context.outputs.size;
762
+ await engine.executePrimitive(action, context, new Map());
763
+ const outputsAfter = context.outputs.size;
764
+ expect(outputsAfter).toBe(outputsBefore);
765
+ });
766
+ it('should throw on invalid bytecode', async () => {
767
+ const action = {
768
+ type: 'create-contract',
769
+ arguments: {
770
+ data: 'invalid-bytecode'
771
+ }
772
+ };
773
+ await expect(engine.executePrimitive(action, context, new Map()))
774
+ .rejects.toThrow();
775
+ });
776
+ });
777
+ describe('send-signed-transaction', () => {
778
+ it('should broadcast a signed transaction', async () => {
779
+ const wallet = new ethers_1.ethers.Wallet(TEST_PRIVATE_KEY, anvilProvider);
780
+ const tx = await wallet.populateTransaction({
781
+ to: TEST_ADDRESSES.RECIPIENT_1,
782
+ value: ethers_1.ethers.parseEther('1'),
783
+ gasLimit: 21000
784
+ });
785
+ const signedTx = await wallet.signTransaction(tx);
786
+ const action = {
787
+ type: 'send-signed-transaction',
788
+ name: 'signed-tx',
789
+ arguments: {
790
+ transaction: signedTx
791
+ }
792
+ };
793
+ await engine.executePrimitive(action, context, new Map());
794
+ expect(context.getOutput('signed-tx.hash')).toBeDefined();
795
+ expect(context.getOutput('signed-tx.receipt')).toBeDefined();
796
+ });
797
+ it('should resolve transaction from context', async () => {
798
+ const wallet = new ethers_1.ethers.Wallet(TEST_PRIVATE_KEY, anvilProvider);
799
+ const tx = await wallet.populateTransaction({
800
+ to: TEST_ADDRESSES.RECIPIENT_1,
801
+ value: ethers_1.ethers.parseEther('1'),
802
+ gasLimit: 21000
803
+ });
804
+ const signedTx = await wallet.signTransaction(tx);
805
+ context.setOutput('prepared_tx', signedTx);
806
+ const action = {
807
+ type: 'send-signed-transaction',
808
+ name: 'resolved-signed-tx',
809
+ arguments: {
810
+ transaction: '{{prepared_tx}}'
811
+ }
812
+ };
813
+ await engine.executePrimitive(action, context, new Map());
814
+ expect(context.getOutput('resolved-signed-tx.hash')).toBeDefined();
815
+ });
816
+ });
817
+ describe('static', () => {
818
+ it('should return the provided value unchanged', async () => {
819
+ const action = {
820
+ type: 'static',
821
+ name: 'test-static',
822
+ arguments: {
823
+ value: 'hello world'
824
+ }
825
+ };
826
+ await engine.executePrimitive(action, context, new Map());
827
+ expect(context.getOutput('test-static.value')).toBe('hello world');
828
+ });
829
+ it('should resolve and return complex values', async () => {
830
+ context.setOutput('input_value', { foo: 'bar', number: 42 });
831
+ const action = {
832
+ type: 'static',
833
+ name: 'complex-static',
834
+ arguments: {
835
+ value: '{{input_value}}'
836
+ }
837
+ };
838
+ await engine.executePrimitive(action, context, new Map());
839
+ expect(context.getOutput('complex-static.value')).toEqual({ foo: 'bar', number: 42 });
840
+ });
841
+ it('should work with numeric values', async () => {
842
+ const action = {
843
+ type: 'static',
844
+ name: 'numeric-static',
845
+ arguments: {
846
+ value: 12345
847
+ }
848
+ };
849
+ await engine.executePrimitive(action, context, new Map());
850
+ expect(context.getOutput('numeric-static.value')).toBe(12345);
851
+ });
852
+ it('should work with boolean values', async () => {
853
+ const action = {
854
+ type: 'static',
855
+ name: 'boolean-static',
856
+ arguments: {
857
+ value: true
858
+ }
859
+ };
860
+ await engine.executePrimitive(action, context, new Map());
861
+ expect(context.getOutput('boolean-static.value')).toBe(true);
862
+ });
863
+ it('should work with array values', async () => {
864
+ const testArray = [1, 2, 3, 'test'];
865
+ const action = {
866
+ type: 'static',
867
+ name: 'array-static',
868
+ arguments: {
869
+ value: testArray
870
+ }
871
+ };
872
+ await engine.executePrimitive(action, context, new Map());
873
+ expect(context.getOutput('array-static.value')).toEqual(testArray);
874
+ });
875
+ it('should not store outputs when action has no name', async () => {
876
+ const action = {
877
+ type: 'static',
878
+ arguments: {
879
+ value: 'test value'
880
+ }
881
+ };
882
+ const outputsBefore = Object.keys(context.outputs || {}).length;
883
+ await engine.executePrimitive(action, context, new Map());
884
+ const outputsAfter = Object.keys(context.outputs || {}).length;
885
+ expect(outputsAfter).toBe(outputsBefore);
886
+ });
887
+ it('should resolve template variables in scope', async () => {
888
+ const scope = new Map();
889
+ scope.set('template_var', 'resolved from scope');
890
+ const action = {
891
+ type: 'static',
892
+ name: 'scope-static',
893
+ arguments: {
894
+ value: '{{template_var}}'
895
+ }
896
+ };
897
+ await engine.executePrimitive(action, context, scope);
898
+ expect(context.getOutput('scope-static.value')).toBe('resolved from scope');
899
+ });
900
+ });
901
+ it('should throw on unknown primitive action type', async () => {
902
+ const action = {
903
+ type: 'unknown-action',
904
+ name: 'unknown',
905
+ arguments: {}
906
+ };
907
+ await expect(engine.executePrimitive(action, context, new Map()))
908
+ .rejects.toThrow('Unknown or unimplemented primitive action type: unknown-action');
909
+ });
910
+ it('should handle custom outputs for primitive actions', async () => {
911
+ const action = {
912
+ name: 'custom-primitive',
913
+ type: 'send-transaction',
914
+ arguments: {
915
+ to: TEST_ADDRESSES.RECIPIENT_1,
916
+ value: TEST_VALUES.ONE_ETH,
917
+ data: '0x'
918
+ },
919
+ output: {
920
+ value: '0xDE280948Af8A9762B6984995C8c3c7F5AEB921Bf'
921
+ }
922
+ };
923
+ await engine.executeAction(action, context, new Map());
924
+ expect(context.getOutput('custom-primitive.value')).toBe('0xDE280948Af8A9762B6984995C8c3c7F5AEB921Bf');
925
+ expect(() => context.getOutput('custom-primitive.hash')).toThrow();
926
+ expect(() => context.getOutput('custom-primitive.receipt')).toThrow();
927
+ });
928
+ });
929
+ describe('evaluateSkipConditions', () => {
930
+ it('should return false for undefined conditions', async () => {
931
+ const result = await engine.evaluateSkipConditions(undefined, context, new Map());
932
+ expect(result).toBe(false);
933
+ });
934
+ it('should return false for empty conditions array', async () => {
935
+ const result = await engine.evaluateSkipConditions([], context, new Map());
936
+ expect(result).toBe(false);
937
+ });
938
+ it('should return true if any condition is met', async () => {
939
+ context.setOutput('flag1', 0);
940
+ context.setOutput('flag2', 1);
941
+ const conditions = [
942
+ { type: 'basic-arithmetic', arguments: { operation: 'eq', values: ['{{flag1}}', 1] } },
943
+ { type: 'basic-arithmetic', arguments: { operation: 'eq', values: ['{{flag2}}', 1] } }
944
+ ];
945
+ const result = await engine.evaluateSkipConditions(conditions, context, new Map());
946
+ expect(result).toBe(true);
947
+ });
948
+ it('should return false if no conditions are met', async () => {
949
+ context.setOutput('flag1', 0);
950
+ context.setOutput('flag2', 0);
951
+ const conditions = [
952
+ { type: 'basic-arithmetic', arguments: { operation: 'eq', values: ['{{flag1}}', 1] } },
953
+ { type: 'basic-arithmetic', arguments: { operation: 'eq', values: ['{{flag2}}', 1] } }
954
+ ];
955
+ const result = await engine.evaluateSkipConditions(conditions, context, new Map());
956
+ expect(result).toBe(false);
957
+ });
958
+ });
959
+ describe('topologicalSortActions', () => {
960
+ it('should sort actions with no dependencies', async () => {
961
+ const job = {
962
+ name: 'no-deps-job',
963
+ version: '1.0.0',
964
+ actions: [
965
+ { name: 'action-c', template: 'send-transaction', arguments: {} },
966
+ { name: 'action-a', template: 'send-transaction', arguments: {} },
967
+ { name: 'action-b', template: 'send-transaction', arguments: {} }
968
+ ]
969
+ };
970
+ const result = engine.topologicalSortActions(job);
971
+ expect(result).toEqual(['action-c', 'action-a', 'action-b']);
972
+ });
973
+ it('should sort actions with dependencies correctly', async () => {
974
+ const job = {
975
+ name: 'deps-job',
976
+ version: '1.0.0',
977
+ actions: [
978
+ { name: 'action-c', template: 'send-transaction', arguments: {}, depends_on: ['action-a', 'action-b'] },
979
+ { name: 'action-b', template: 'send-transaction', arguments: {}, depends_on: ['action-a'] },
980
+ { name: 'action-a', template: 'send-transaction', arguments: {} }
981
+ ]
982
+ };
983
+ const result = engine.topologicalSortActions(job);
984
+ expect(result).toEqual(['action-a', 'action-b', 'action-c']);
985
+ });
986
+ it('should throw on circular dependencies', async () => {
987
+ const job = {
988
+ name: 'circular-job',
989
+ version: '1.0.0',
990
+ actions: [
991
+ { name: 'action-a', template: 'send-transaction', arguments: {}, depends_on: ['action-b'] },
992
+ { name: 'action-b', template: 'send-transaction', arguments: {}, depends_on: ['action-a'] }
993
+ ]
994
+ };
995
+ expect(() => engine.topologicalSortActions(job))
996
+ .toThrow('Circular dependency detected among actions in job "circular-job".');
997
+ });
998
+ it('should throw on invalid dependency', async () => {
999
+ const job = {
1000
+ name: 'invalid-dep-job',
1001
+ version: '1.0.0',
1002
+ actions: [
1003
+ { name: 'action-a', template: 'send-transaction', arguments: {}, depends_on: ['non-existent'] }
1004
+ ]
1005
+ };
1006
+ expect(() => engine.topologicalSortActions(job))
1007
+ .toThrow('Action "action-a" in job "invalid-dep-job" has an invalid dependency on "non-existent", which does not exist.');
1008
+ });
1009
+ });
1010
+ describe('integration tests', () => {
1011
+ it('should execute a complex job with templates, dependencies, and skip conditions', async () => {
1012
+ const executedActions = [];
1013
+ const originalExecuteAction = engine.executeAction;
1014
+ engine.executeAction = async function (action, ctx, scope) {
1015
+ const actionName = 'name' in action ? action.name : action.type;
1016
+ if (actionName) {
1017
+ executedActions.push(actionName);
1018
+ if (actionName === 'setup-step') {
1019
+ ctx.setOutput('fund-contract.hash', 'mock-fund-hash');
1020
+ ctx.setOutput('fund-contract.receipt', { status: 1, blockNumber: 200 });
1021
+ ctx.setOutput('setup-step.funded_address', '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC');
1022
+ ctx.setOutput('setup-step.fund_amount', '2000000000000000000');
1023
+ }
1024
+ else if (actionName === 'deploy-step') {
1025
+ ctx.setOutput('deploy-contract.hash', 'mock-deploy-hash');
1026
+ ctx.setOutput('deploy-contract.receipt', { status: 1, blockNumber: 201 });
1027
+ ctx.setOutput('deploy-step.deployment_hash', 'mock-deploy-hash');
1028
+ }
1029
+ else if (actionName === 'final-check') {
1030
+ ctx.setOutput('final-check.hash', 'mock-final-hash');
1031
+ ctx.setOutput('final-check.receipt', { status: 1, blockNumber: 202 });
1032
+ }
1033
+ }
1034
+ };
1035
+ const setupTemplate = {
1036
+ name: 'setup-template',
1037
+ actions: [
1038
+ {
1039
+ type: 'send-transaction',
1040
+ name: 'fund-contract',
1041
+ arguments: {
1042
+ to: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC',
1043
+ value: '2000000000000000000',
1044
+ data: '0x'
1045
+ }
1046
+ }
1047
+ ],
1048
+ outputs: {
1049
+ funded_address: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC',
1050
+ fund_amount: '2000000000000000000'
1051
+ }
1052
+ };
1053
+ const deployTemplate = {
1054
+ name: 'deploy-template',
1055
+ actions: [
1056
+ {
1057
+ type: 'send-transaction',
1058
+ name: 'deploy-contract',
1059
+ arguments: {
1060
+ to: '0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65',
1061
+ value: '1000000000000000000',
1062
+ data: '0x608060405234801561000f575f5ffd5b50603e80601c5f395ff3fe60806040525f80fdfea264697066735822122071d40daa3d2beacd91f29d29ccf1c0b6f312e805f50b37166267c0a2a55e6e6164736f6c634300081c0033'
1063
+ }
1064
+ }
1065
+ ],
1066
+ outputs: {
1067
+ deployment_hash: '{{deploy-contract.hash}}'
1068
+ }
1069
+ };
1070
+ templates.set('setup-template', setupTemplate);
1071
+ templates.set('deploy-template', deployTemplate);
1072
+ const complexJob = {
1073
+ name: 'complex-job',
1074
+ version: '1.0.0',
1075
+ actions: [
1076
+ {
1077
+ name: 'deploy-step',
1078
+ template: 'deploy-template',
1079
+ arguments: {
1080
+ funded_address: '{{setup-step.funded_address}}',
1081
+ fund_amount: '{{setup-step.fund_amount}}'
1082
+ },
1083
+ depends_on: ['setup-step']
1084
+ },
1085
+ {
1086
+ name: 'setup-step',
1087
+ template: 'setup-template',
1088
+ arguments: {}
1089
+ },
1090
+ {
1091
+ name: 'final-check',
1092
+ template: 'send-transaction',
1093
+ arguments: {
1094
+ to: '0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc',
1095
+ value: '1000000000000000000',
1096
+ data: '0x'
1097
+ },
1098
+ depends_on: ['deploy-step']
1099
+ }
1100
+ ]
1101
+ };
1102
+ await engine.executeJob(complexJob, context);
1103
+ engine.executeAction = originalExecuteAction;
1104
+ expect(executedActions).toEqual(['setup-step', 'deploy-step', 'final-check']);
1105
+ expect(context.getOutput('setup-step.funded_address')).toBe('0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC');
1106
+ expect(context.getOutput('setup-step.fund_amount')).toBe('2000000000000000000');
1107
+ expect(context.getOutput('deploy-step.deployment_hash')).toBe('mock-deploy-hash');
1108
+ expect(context.getOutput('final-check.hash')).toBe('mock-final-hash');
1109
+ });
1110
+ it('should handle failed transactions appropriately', async () => {
1111
+ const job = {
1112
+ name: 'failing-job',
1113
+ version: '1.0.0',
1114
+ actions: [
1115
+ {
1116
+ name: 'failing-tx',
1117
+ template: 'send-transaction',
1118
+ arguments: {
1119
+ to: 'not-an-address',
1120
+ value: '1000000000000000000',
1121
+ data: '0x'
1122
+ }
1123
+ }
1124
+ ]
1125
+ };
1126
+ await expect(engine.executeJob(job, context)).rejects.toThrow();
1127
+ });
1128
+ });
1129
+ describe('setup skip conditions', () => {
1130
+ it('should skip setup actions when contract-exists condition is met', async () => {
1131
+ const contractAddress = '0x5FbDB2315678afecb367f032d93F642f64180aa3';
1132
+ const miniContractBytecode = '0x608060405234801561000f575f5ffd5b5060043610610034575f3560e01c80636df5b97a14610038578063f8a8fd6d14610068575b5f5ffd5b610052600480360381019061004d91906100da565b610086565b60405161005f9190610127565b60405180910390f35b61007061009b565b60405161007d9190610127565b60405180910390f35b5f8183610093919061016d565b905092915050565b5f602a905090565b5f5ffd5b5f819050919050565b6100b9816100a7565b81146100c3575f5ffd5b50565b5f813590506100d4816100b0565b92915050565b5f5f604083850312156100f0576100ef6100a3565b5b5f6100fd858286016100c6565b925050602061010e858286016100c6565b9150509250929050565b610121816100a7565b82525050565b5f60208201905061013a5f830184610118565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610177826100a7565b9150610182836100a7565b9250828202610190816100a7565b915082820484148315176101a7576101a6610140565b5b509291505056fea264697066735822122071d40daa3d2beacd91f29d29ccf1c0b6f312e805f50b37166267c0a2a55e6e6164736f6c634300081c0033';
1133
+ await anvilProvider.send('anvil_setCode', [contractAddress, miniContractBytecode]);
1134
+ const template = {
1135
+ name: 'test-contract-exists-skip',
1136
+ setup: {
1137
+ skip_condition: [
1138
+ { type: 'contract-exists', arguments: { address: contractAddress } }
1139
+ ],
1140
+ actions: [
1141
+ {
1142
+ type: 'send-transaction',
1143
+ name: 'setup-action',
1144
+ arguments: {
1145
+ to: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
1146
+ value: '1000000000000000000',
1147
+ data: '0x'
1148
+ }
1149
+ }
1150
+ ]
1151
+ },
1152
+ actions: [
1153
+ {
1154
+ type: 'send-transaction',
1155
+ name: 'main-action',
1156
+ arguments: {
1157
+ to: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC',
1158
+ value: '500000000000000000',
1159
+ data: '0x'
1160
+ }
1161
+ }
1162
+ ]
1163
+ };
1164
+ templates.set('test-contract-exists-skip', template);
1165
+ const action = {
1166
+ name: 'test-skip',
1167
+ template: 'test-contract-exists-skip',
1168
+ arguments: {}
1169
+ };
1170
+ await engine.executeAction(action, context, new Map());
1171
+ expect(() => context.getOutput('setup-action.hash')).toThrow();
1172
+ expect(context.getOutput('main-action.hash')).toBeDefined();
1173
+ });
1174
+ });
1175
+ describe('test-nicks-method action', () => {
1176
+ it('should successfully test Nick\'s method with a simple contract', async () => {
1177
+ const action = {
1178
+ type: 'test-nicks-method',
1179
+ name: 'nick-test',
1180
+ arguments: {
1181
+ bytecode: TEST_BYTECODES.SIMPLE_RETURN_42,
1182
+ gasPrice: ethers_1.ethers.parseUnits('10', 'gwei'),
1183
+ gasLimit: 100000n,
1184
+ fundingAmount: ethers_1.ethers.parseEther('0.001')
1185
+ }
1186
+ };
1187
+ await engine.executeAction(action, context, new Map());
1188
+ expect(context.getOutput('nick-test.success')).toBe(true);
1189
+ });
1190
+ it('should handle missing optional parameters', async () => {
1191
+ const action = {
1192
+ type: 'test-nicks-method',
1193
+ name: 'nick-test-defaults',
1194
+ arguments: {
1195
+ bytecode: TEST_BYTECODES.SIMPLE_RETURN_42
1196
+ }
1197
+ };
1198
+ await engine.executeAction(action, context, new Map());
1199
+ expect(context.getOutput('nick-test-defaults.success')).toBe(true);
1200
+ });
1201
+ it('should use default bytecode when none provided', async () => {
1202
+ const action = {
1203
+ type: 'test-nicks-method',
1204
+ name: 'nick-test-default-bytecode',
1205
+ arguments: {}
1206
+ };
1207
+ await engine.executeAction(action, context, new Map());
1208
+ expect(context.getOutput('nick-test-default-bytecode.success')).toBe(true);
1209
+ });
1210
+ });
1211
+ });
1212
+ //# sourceMappingURL=engine.spec.js.map