@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,1693 @@
1
+ import { ethers } from 'ethers'
2
+ import { ValueResolver } from '../resolver'
3
+ import { ExecutionContext } from '../context'
4
+ import { BasicArithmeticValue, Network, ReadBalanceValue, ComputeCreate2Value, ConstructorEncodeValue, AbiEncodeValue, AbiPackValue, CallValue, ContractExistsValue } from '../../types'
5
+ import { ContractRepository } from '../../contracts/repository'
6
+
7
+ describe('ValueResolver', () => {
8
+ let resolver: ValueResolver
9
+ let context: ExecutionContext
10
+ let mockNetwork: Network
11
+ let mockRegistry: ContractRepository
12
+
13
+ beforeEach(async () => {
14
+ resolver = new ValueResolver()
15
+ mockRegistry = new ContractRepository()
16
+ // Allow configuring RPC URL via environment variable for CI
17
+ const rpcUrl = process.env.RPC_URL || 'http://127.0.0.1:8545'
18
+ mockNetwork = { name: 'testnet', chainId: 999, rpcUrl }
19
+ // A dummy private key is fine as these tests don't send transactions
20
+ const mockPrivateKey = '0x0000000000000000000000000000000000000000000000000000000000000001'
21
+ context = new ExecutionContext(mockNetwork, mockPrivateKey, mockRegistry)
22
+
23
+ // Try to connect to the node, fail immediately if not available
24
+ await (context.provider as ethers.JsonRpcProvider).getNetwork()
25
+ })
26
+
27
+ afterEach(async () => {
28
+ // Clean up context to prevent hanging connections
29
+ if (context) {
30
+ try {
31
+ await context.dispose()
32
+ } catch (error) {
33
+ // Ignore cleanup errors
34
+ }
35
+ }
36
+ })
37
+
38
+ describe('basic-arithmetic', () => {
39
+ it('should add two numbers', async () => {
40
+ const value: BasicArithmeticValue = {
41
+ type: 'basic-arithmetic',
42
+ arguments: { operation: 'add', values: ["100", "50"] },
43
+ }
44
+ const result = await resolver.resolve(value, context)
45
+ expect(result).toBe('150')
46
+ })
47
+
48
+ it('should subtract two numbers', async () => {
49
+ const value: BasicArithmeticValue = {
50
+ type: 'basic-arithmetic',
51
+ arguments: { operation: 'sub', values: [100, 50] },
52
+ }
53
+ const result = await resolver.resolve(value, context)
54
+ expect(result).toBe('50')
55
+ })
56
+
57
+ it('should multiply two numbers', async () => {
58
+ const value: BasicArithmeticValue = {
59
+ type: 'basic-arithmetic',
60
+ arguments: { operation: 'mul', values: [10, 5] },
61
+ }
62
+ const result = await resolver.resolve(value, context)
63
+ expect(result).toBe('50')
64
+ })
65
+
66
+ it('should divide two numbers (integer division)', async () => {
67
+ const value: BasicArithmeticValue = {
68
+ type: 'basic-arithmetic',
69
+ arguments: { operation: 'div', values: [10, 3] },
70
+ }
71
+ const result = await resolver.resolve(value, context)
72
+ expect(result).toBe('3')
73
+ })
74
+
75
+ it('should handle large numbers as strings', async () => {
76
+ const value: BasicArithmeticValue = {
77
+ type: 'basic-arithmetic',
78
+ arguments: { operation: 'add', values: ['10000000000000000000', '5000000000000000000'] },
79
+ }
80
+ const result = await resolver.resolve(value, context)
81
+ expect(result).toBe('15000000000000000000')
82
+ })
83
+
84
+ it('should correctly evaluate "eq" (equal)', async () => {
85
+ const valueTrue: BasicArithmeticValue = { type: 'basic-arithmetic', arguments: { operation: 'eq', values: [10, 10] } }
86
+ const valueFalse: BasicArithmeticValue = { type: 'basic-arithmetic', arguments: { operation: 'eq', values: [10, 5] } }
87
+ expect(await resolver.resolve(valueTrue, context)).toBe(true)
88
+ expect(await resolver.resolve(valueFalse, context)).toBe(false)
89
+ })
90
+
91
+ it('should correctly evaluate "neq" (not equal)', async () => {
92
+ const valueTrue: BasicArithmeticValue = { type: 'basic-arithmetic', arguments: { operation: 'neq', values: [10, 5] } }
93
+ const valueFalse: BasicArithmeticValue = { type: 'basic-arithmetic', arguments: { operation: 'neq', values: [10, 10] } }
94
+ expect(await resolver.resolve(valueTrue, context)).toBe(true)
95
+ expect(await resolver.resolve(valueFalse, context)).toBe(false)
96
+ })
97
+
98
+ it('should correctly evaluate "gt" (greater than)', async () => {
99
+ const valueTrue: BasicArithmeticValue = { type: 'basic-arithmetic', arguments: { operation: 'gt', values: [10, 5] } }
100
+ const valueFalse: BasicArithmeticValue = { type: 'basic-arithmetic', arguments: { operation: 'gt', values: [10, 10] } }
101
+ expect(await resolver.resolve(valueTrue, context)).toBe(true)
102
+ expect(await resolver.resolve(valueFalse, context)).toBe(false)
103
+ })
104
+
105
+ it('should correctly evaluate "gte" (greater than or equal)', async () => {
106
+ const valueTrue1: BasicArithmeticValue = { type: 'basic-arithmetic', arguments: { operation: 'gte', values: [10, 5] } }
107
+ const valueTrue2: BasicArithmeticValue = { type: 'basic-arithmetic', arguments: { operation: 'gte', values: [10, 10] } }
108
+ const valueFalse: BasicArithmeticValue = { type: 'basic-arithmetic', arguments: { operation: 'gte', values: [5, 10] } }
109
+ expect(await resolver.resolve(valueTrue1, context)).toBe(true)
110
+ expect(await resolver.resolve(valueTrue2, context)).toBe(true)
111
+ expect(await resolver.resolve(valueFalse, context)).toBe(false)
112
+ })
113
+
114
+ it('should correctly evaluate "lt" (less than)', async () => {
115
+ const valueTrue: BasicArithmeticValue = { type: 'basic-arithmetic', arguments: { operation: 'lt', values: [5, 10] } }
116
+ const valueFalse: BasicArithmeticValue = { type: 'basic-arithmetic', arguments: { operation: 'lt', values: [10, 10] } }
117
+ expect(await resolver.resolve(valueTrue, context)).toBe(true)
118
+ expect(await resolver.resolve(valueFalse, context)).toBe(false)
119
+ })
120
+
121
+ it('should correctly evaluate "lte" (less than or equal)', async () => {
122
+ const valueTrue1: BasicArithmeticValue = { type: 'basic-arithmetic', arguments: { operation: 'lte', values: [5, 10] } }
123
+ const valueTrue2: BasicArithmeticValue = { type: 'basic-arithmetic', arguments: { operation: 'lte', values: [10, 10] } }
124
+ const valueFalse: BasicArithmeticValue = { type: 'basic-arithmetic', arguments: { operation: 'lte', values: [10, 5] } }
125
+ expect(await resolver.resolve(valueTrue1, context)).toBe(true)
126
+ expect(await resolver.resolve(valueTrue2, context)).toBe(true)
127
+ expect(await resolver.resolve(valueFalse, context)).toBe(false)
128
+ })
129
+
130
+ it('should resolve nested values before performing the operation', async () => {
131
+ context.setOutput('fee', '10000000000000000') // Set a value in the context
132
+ const value: BasicArithmeticValue = {
133
+ type: 'basic-arithmetic',
134
+ arguments: { operation: 'add', values: ['{{fee}}', '5000000000000000'] },
135
+ }
136
+ const result = await resolver.resolve(value, context)
137
+ expect(result).toBe('15000000000000000')
138
+ })
139
+ })
140
+
141
+ describe('read-balance', () => {
142
+ const testAddress = '0x1234567890123456789012345678901234567890'
143
+ const testAddress2 = '0x0987654321098765432109876543210987654321'
144
+ let anvilProvider: ethers.JsonRpcProvider
145
+
146
+ beforeEach(async () => {
147
+ anvilProvider = context.provider as ethers.JsonRpcProvider
148
+
149
+ // Reset balances before each test
150
+ await anvilProvider.send('anvil_setBalance', [testAddress, '0x0'])
151
+ await anvilProvider.send('anvil_setBalance', [testAddress2, '0x0'])
152
+ })
153
+
154
+ it('should read balance for an address with 0 ETH', async () => {
155
+ const value: ReadBalanceValue = {
156
+ type: 'read-balance',
157
+ arguments: { address: testAddress },
158
+ }
159
+ const result = await resolver.resolve(value, context)
160
+ expect(result).toBe('0')
161
+ })
162
+
163
+ it('should read balance for an address with 1 ETH', async () => {
164
+ // Set balance to 1 ETH (1e18 wei)
165
+ await anvilProvider.send('anvil_setBalance', [testAddress, '0xde0b6b3a7640000'])
166
+
167
+ const value: ReadBalanceValue = {
168
+ type: 'read-balance',
169
+ arguments: { address: testAddress },
170
+ }
171
+ const result = await resolver.resolve(value, context)
172
+ expect(result).toBe('1000000000000000000')
173
+ })
174
+
175
+ it('should read balance for an address with 100 ETH', async () => {
176
+ // Set balance to 100 ETH (100e18 wei)
177
+ await anvilProvider.send('anvil_setBalance', [testAddress, '0x56bc75e2d63100000'])
178
+
179
+ const value: ReadBalanceValue = {
180
+ type: 'read-balance',
181
+ arguments: { address: testAddress },
182
+ }
183
+ const result = await resolver.resolve(value, context)
184
+ expect(result).toBe('100000000000000000000')
185
+ })
186
+
187
+ it('should read balance for an address with custom amount', async () => {
188
+ // Set balance to 12.345 ETH (12345000000000000000 wei)
189
+ await anvilProvider.send('anvil_setBalance', [testAddress, '0xab524017e8328000'])
190
+
191
+ const value: ReadBalanceValue = {
192
+ type: 'read-balance',
193
+ arguments: { address: testAddress },
194
+ }
195
+ const result = await resolver.resolve(value, context)
196
+ expect(result).toBe('12345000000000000000')
197
+ })
198
+
199
+ it('should read different balances for different addresses', async () => {
200
+ // Set different balances for two addresses
201
+ await anvilProvider.send('anvil_setBalance', [testAddress, '0xde0b6b3a7640000']) // 1 ETH
202
+ await anvilProvider.send('anvil_setBalance', [testAddress2, '0x1bc16d674ec80000']) // 2 ETH
203
+
204
+ const value1: ReadBalanceValue = {
205
+ type: 'read-balance',
206
+ arguments: { address: testAddress },
207
+ }
208
+ const value2: ReadBalanceValue = {
209
+ type: 'read-balance',
210
+ arguments: { address: testAddress2 },
211
+ }
212
+
213
+ const result1 = await resolver.resolve(value1, context)
214
+ const result2 = await resolver.resolve(value2, context)
215
+
216
+ expect(result1).toBe('1000000000000000000')
217
+ expect(result2).toBe('2000000000000000000')
218
+ })
219
+
220
+ it('should resolve address from context variable', async () => {
221
+ context.setOutput('myAddress', testAddress)
222
+ await anvilProvider.send('anvil_setBalance', [testAddress, '0xde0b6b3a7640000']) // 1 ETH
223
+
224
+ const value: ReadBalanceValue = {
225
+ type: 'read-balance',
226
+ arguments: { address: '{{myAddress}}' },
227
+ }
228
+ const result = await resolver.resolve(value, context)
229
+ expect(result).toBe('1000000000000000000')
230
+ })
231
+
232
+ it('should throw error for invalid address', async () => {
233
+ const value: ReadBalanceValue = {
234
+ type: 'read-balance',
235
+ arguments: { address: 'invalid-address' },
236
+ }
237
+
238
+ await expect(resolver.resolve(value, context)).rejects.toThrow('Invalid address: invalid-address')
239
+ })
240
+
241
+ it('should throw error for null address', async () => {
242
+ const value: ReadBalanceValue = {
243
+ type: 'read-balance',
244
+ arguments: { address: null as any },
245
+ }
246
+
247
+ await expect(resolver.resolve(value, context)).rejects.toThrow('Invalid address: null')
248
+ })
249
+
250
+ it('should throw error for undefined address', async () => {
251
+ const value: ReadBalanceValue = {
252
+ type: 'read-balance',
253
+ arguments: { address: undefined as any },
254
+ }
255
+
256
+ await expect(resolver.resolve(value, context)).rejects.toThrow('Invalid address: undefined')
257
+ })
258
+
259
+ it('should handle very large balance amounts', async () => {
260
+ // Set a very large balance (max uint256)
261
+ await anvilProvider.send('anvil_setBalance', [testAddress, '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'])
262
+
263
+ const value: ReadBalanceValue = {
264
+ type: 'read-balance',
265
+ arguments: { address: testAddress },
266
+ }
267
+ const result = await resolver.resolve(value, context)
268
+ expect(result).toBe('115792089237316195423570985008687907853269984665640564039457584007913129639935')
269
+ })
270
+
271
+ it('should handle checksummed addresses', async () => {
272
+ const checksummedAddress = '0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed'
273
+ await anvilProvider.send('anvil_setBalance', [checksummedAddress, '0xde0b6b3a7640000']) // 1 ETH
274
+
275
+ const value: ReadBalanceValue = {
276
+ type: 'read-balance',
277
+ arguments: { address: checksummedAddress },
278
+ }
279
+ const result = await resolver.resolve(value, context)
280
+ expect(result).toBe('1000000000000000000')
281
+ })
282
+
283
+ it('should handle lowercase addresses', async () => {
284
+ const lowercaseAddress = testAddress.toLowerCase()
285
+ await anvilProvider.send('anvil_setBalance', [lowercaseAddress, '0xde0b6b3a7640000']) // 1 ETH
286
+
287
+ const value: ReadBalanceValue = {
288
+ type: 'read-balance',
289
+ arguments: { address: lowercaseAddress },
290
+ }
291
+ const result = await resolver.resolve(value, context)
292
+ expect(result).toBe('1000000000000000000')
293
+ })
294
+ })
295
+
296
+ describe('compute-create2', () => {
297
+ it('should compute CREATE2 address with hardcoded test case 1', async () => {
298
+ const value: ComputeCreate2Value = {
299
+ type: 'compute-create2',
300
+ arguments: {
301
+ deployerAddress: '0x0000000000000000000000000000000000000000',
302
+ salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
303
+ initCode: '0x00',
304
+ },
305
+ }
306
+ const result = await resolver.resolve(value, context)
307
+ expect(result).toBe('0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38')
308
+ })
309
+
310
+ it('should compute CREATE2 address with hardcoded test case 2', async () => {
311
+ const value: ComputeCreate2Value = {
312
+ type: 'compute-create2',
313
+ arguments: {
314
+ deployerAddress: '0xdeadbeef00000000000000000000000000000000',
315
+ salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
316
+ initCode: '0x00',
317
+ },
318
+ }
319
+ const result = await resolver.resolve(value, context)
320
+ expect(result).toBe('0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3')
321
+ })
322
+
323
+ it('should compute CREATE2 address with hardcoded test case 3', async () => {
324
+ const value: ComputeCreate2Value = {
325
+ type: 'compute-create2',
326
+ arguments: {
327
+ deployerAddress: '0xdeadbeef00000000000000000000000000000000',
328
+ salt: '0x000000000000000000000000feed000000000000000000000000000000000000',
329
+ initCode: '0x00',
330
+ },
331
+ }
332
+ const result = await resolver.resolve(value, context)
333
+ expect(result).toBe('0xD04116cDd17beBE565EB2422F2497E06cC1C9833')
334
+ })
335
+
336
+ it('should compute CREATE2 address with hardcoded test case 4', async () => {
337
+ const value: ComputeCreate2Value = {
338
+ type: 'compute-create2',
339
+ arguments: {
340
+ deployerAddress: '0x0000000000000000000000000000000000000000',
341
+ salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
342
+ initCode: '0xdeadbeef',
343
+ },
344
+ }
345
+ const result = await resolver.resolve(value, context)
346
+ expect(result).toBe('0x70f2b2914A2a4b783FaEFb75f459A580616Fcb5e')
347
+ })
348
+
349
+ it('should compute CREATE2 address with hardcoded test case 5', async () => {
350
+ const value: ComputeCreate2Value = {
351
+ type: 'compute-create2',
352
+ arguments: {
353
+ deployerAddress: '0x00000000000000000000000000000000deadbeef',
354
+ salt: '0x00000000000000000000000000000000000000000000000000000000cafebabe',
355
+ initCode: '0xdeadbeef',
356
+ },
357
+ }
358
+ const result = await resolver.resolve(value, context)
359
+ expect(result).toBe('0x60f3f640a8508fC6a86d45DF051962668E1e8AC7')
360
+ })
361
+
362
+ it('should compute CREATE2 address with hardcoded test case 6', async () => {
363
+ const value: ComputeCreate2Value = {
364
+ type: 'compute-create2',
365
+ arguments: {
366
+ deployerAddress: '0x00000000000000000000000000000000deadbeef',
367
+ salt: '0x00000000000000000000000000000000000000000000000000000000cafebabe',
368
+ initCode: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
369
+ },
370
+ }
371
+ const result = await resolver.resolve(value, context)
372
+ expect(result).toBe('0x1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C')
373
+ })
374
+
375
+ it('should compute CREATE2 address with hardcoded test case 7 (empty initCode)', async () => {
376
+ const value: ComputeCreate2Value = {
377
+ type: 'compute-create2',
378
+ arguments: {
379
+ deployerAddress: '0x0000000000000000000000000000000000000000',
380
+ salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
381
+ initCode: '0x',
382
+ },
383
+ }
384
+ const result = await resolver.resolve(value, context)
385
+ expect(result).toBe('0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0')
386
+ })
387
+
388
+ it('should resolve values from context before computing CREATE2 address', async () => {
389
+ context.setOutput('myDeployer', '0x0000000000000000000000000000000000000000')
390
+ context.setOutput('mySalt', '0x0000000000000000000000000000000000000000000000000000000000000000')
391
+ context.setOutput('myInitCode', '0x00')
392
+
393
+ const value: ComputeCreate2Value = {
394
+ type: 'compute-create2',
395
+ arguments: {
396
+ deployerAddress: '{{myDeployer}}',
397
+ salt: '{{mySalt}}',
398
+ initCode: '{{myInitCode}}',
399
+ },
400
+ }
401
+ const result = await resolver.resolve(value, context)
402
+ expect(result).toBe('0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38')
403
+ })
404
+
405
+ it('should throw error for invalid deployer address', async () => {
406
+ const value: ComputeCreate2Value = {
407
+ type: 'compute-create2',
408
+ arguments: {
409
+ deployerAddress: 'invalid-address',
410
+ salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
411
+ initCode: '0x00',
412
+ },
413
+ }
414
+
415
+ await expect(resolver.resolve(value, context)).rejects.toThrow('Invalid deployer address: invalid-address')
416
+ })
417
+
418
+ it('should throw error for invalid salt', async () => {
419
+ const value: ComputeCreate2Value = {
420
+ type: 'compute-create2',
421
+ arguments: {
422
+ deployerAddress: '0x0000000000000000000000000000000000000000',
423
+ salt: 'invalid-salt',
424
+ initCode: '0x00',
425
+ },
426
+ }
427
+
428
+ await expect(resolver.resolve(value, context)).rejects.toThrow('Invalid salt: invalid-salt')
429
+ })
430
+
431
+ it('should throw error for invalid init code', async () => {
432
+ const value: ComputeCreate2Value = {
433
+ type: 'compute-create2',
434
+ arguments: {
435
+ deployerAddress: '0x0000000000000000000000000000000000000000',
436
+ salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
437
+ initCode: 'invalid-init-code',
438
+ },
439
+ }
440
+
441
+ await expect(resolver.resolve(value, context)).rejects.toThrow('Invalid init code: invalid-init-code')
442
+ })
443
+
444
+ it('should throw error for null deployer address', async () => {
445
+ const value: ComputeCreate2Value = {
446
+ type: 'compute-create2',
447
+ arguments: {
448
+ deployerAddress: null as any,
449
+ salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
450
+ initCode: '0x00',
451
+ },
452
+ }
453
+
454
+ await expect(resolver.resolve(value, context)).rejects.toThrow('Invalid deployer address: null')
455
+ })
456
+
457
+ it('should throw error for undefined salt', async () => {
458
+ const value: ComputeCreate2Value = {
459
+ type: 'compute-create2',
460
+ arguments: {
461
+ deployerAddress: '0x0000000000000000000000000000000000000000',
462
+ salt: undefined as any,
463
+ initCode: '0x00',
464
+ },
465
+ }
466
+
467
+ await expect(resolver.resolve(value, context)).rejects.toThrow('Invalid salt: undefined')
468
+ })
469
+
470
+ it('should handle checksummed addresses', async () => {
471
+ const value: ComputeCreate2Value = {
472
+ type: 'compute-create2',
473
+ arguments: {
474
+ deployerAddress: '0xdEADBEeF00000000000000000000000000000000',
475
+ salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
476
+ initCode: '0x00',
477
+ },
478
+ }
479
+ const result = await resolver.resolve(value, context)
480
+ expect(result).toBe('0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3')
481
+ })
482
+
483
+ it('should handle uppercase hex strings', async () => {
484
+ const value: ComputeCreate2Value = {
485
+ type: 'compute-create2',
486
+ arguments: {
487
+ deployerAddress: '0x0000000000000000000000000000000000000000',
488
+ salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
489
+ initCode: '0xDEADBEEF',
490
+ },
491
+ }
492
+ const result = await resolver.resolve(value, context)
493
+ expect(result).toBe('0x70f2b2914A2a4b783FaEFb75f459A580616Fcb5e')
494
+ })
495
+ })
496
+
497
+ describe('constructor-encode', () => {
498
+ it('should encode creation code with no constructor arguments', async () => {
499
+ const value = {
500
+ type: 'constructor-encode' as const,
501
+ arguments: {
502
+ creationCode: '0x608060405234801561001057600080fd5b50',
503
+ types: [],
504
+ values: []
505
+ }
506
+ }
507
+ const result = await resolver.resolve(value, context)
508
+ expect(result).toBe('0x608060405234801561001057600080fd5b50')
509
+ })
510
+
511
+ it('should encode creation code with a single address argument', async () => {
512
+ const value = {
513
+ type: 'constructor-encode' as const,
514
+ arguments: {
515
+ creationCode: '0x608060405234801561001057600080fd5b50',
516
+ types: ['address'],
517
+ values: ['0x1234567890123456789012345678901234567890']
518
+ }
519
+ }
520
+ const result = await resolver.resolve(value, context)
521
+ // Should be creation code + encoded address (padded to 32 bytes)
522
+ expect(result).toBe('0x608060405234801561001057600080fd5b500000000000000000000000001234567890123456789012345678901234567890')
523
+ })
524
+
525
+ it('should encode creation code with multiple arguments', async () => {
526
+ const value = {
527
+ type: 'constructor-encode' as const,
528
+ arguments: {
529
+ creationCode: '0x608060405234801561001057600080fd5b50',
530
+ types: ['address', 'uint256'],
531
+ values: ['0x1234567890123456789012345678901234567890', '42']
532
+ }
533
+ }
534
+ const result = await resolver.resolve(value, context) as string
535
+ // Should concatenate creation code with ABI-encoded constructor args
536
+ expect(result.startsWith('0x608060405234801561001057600080fd5b50')).toBe(true)
537
+ expect(result.length).toBeGreaterThan('0x608060405234801561001057600080fd5b50'.length)
538
+ })
539
+
540
+ it('should validate that types and values arrays have same length', async () => {
541
+ const value = {
542
+ type: 'constructor-encode' as const,
543
+ arguments: {
544
+ creationCode: '0x608060405234801561001057600080fd5b50',
545
+ types: ['address'],
546
+ values: ['0x1234567890123456789012345678901234567890', '42'] // extra value
547
+ }
548
+ }
549
+
550
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
551
+ 'constructor-encode: types array length (1) must match values array length (2)'
552
+ )
553
+ })
554
+
555
+ it('should validate creation code is valid bytecode', async () => {
556
+ const value = {
557
+ type: 'constructor-encode' as const,
558
+ arguments: {
559
+ creationCode: 'not-valid-bytecode',
560
+ types: [],
561
+ values: []
562
+ }
563
+ }
564
+
565
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
566
+ 'Invalid creation code: not-valid-bytecode'
567
+ )
568
+ })
569
+
570
+ it('should encode just constructor arguments when no creationCode provided', async () => {
571
+ const value = {
572
+ type: 'constructor-encode' as const,
573
+ arguments: {
574
+ types: ['address'],
575
+ values: ['0x1234567890123456789012345678901234567890']
576
+ }
577
+ }
578
+ const result = await resolver.resolve(value, context)
579
+ // Should just be the ABI-encoded address (padded to 32 bytes with 0x prefix)
580
+ expect(result).toBe('0x0000000000000000000000001234567890123456789012345678901234567890')
581
+ })
582
+
583
+ it('should encode multiple constructor arguments when no creationCode provided', async () => {
584
+ const value = {
585
+ type: 'constructor-encode' as const,
586
+ arguments: {
587
+ types: ['address', 'uint256'],
588
+ values: ['0x1234567890123456789012345678901234567890', '42']
589
+ }
590
+ }
591
+ const result = await resolver.resolve(value, context) as string
592
+ // Should be ABI-encoded constructor args only
593
+ expect(result.startsWith('0x')).toBe(true)
594
+ // Address should be encoded first (32 bytes), then uint256 (32 bytes)
595
+ expect(result).toBe('0x0000000000000000000000001234567890123456789012345678901234567890000000000000000000000000000000000000000000000000000000000000002a')
596
+ })
597
+
598
+ it('should return 0x when no creationCode and no constructor arguments', async () => {
599
+ const value = {
600
+ type: 'constructor-encode' as const,
601
+ arguments: {
602
+ types: [],
603
+ values: []
604
+ }
605
+ }
606
+ const result = await resolver.resolve(value, context)
607
+ expect(result).toBe('0x')
608
+ })
609
+
610
+ it('should validate that types and values arrays have same length when no creationCode', async () => {
611
+ const value = {
612
+ type: 'constructor-encode' as const,
613
+ arguments: {
614
+ types: ['address'],
615
+ values: ['0x1234567890123456789012345678901234567890', '42'] // extra value
616
+ }
617
+ }
618
+
619
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
620
+ 'constructor-encode: types array length (1) must match values array length (2)'
621
+ )
622
+ })
623
+ })
624
+
625
+ describe('abi-encode', () => {
626
+ it('should encode a simple function with no parameters', async () => {
627
+ const value: AbiEncodeValue = {
628
+ type: 'abi-encode',
629
+ arguments: {
630
+ signature: 'withdraw()',
631
+ values: []
632
+ }
633
+ }
634
+ const result = await resolver.resolve(value, context) as string
635
+ // Function selector for withdraw() should be the first 4 bytes of keccak256("withdraw()")
636
+ expect(result).toBe('0x3ccfd60b')
637
+ })
638
+
639
+ it('should encode a function with a single address parameter', async () => {
640
+ const value: AbiEncodeValue = {
641
+ type: 'abi-encode',
642
+ arguments: {
643
+ signature: 'transfer(address)',
644
+ values: ['0x1234567890123456789012345678901234567890']
645
+ }
646
+ }
647
+ const result = await resolver.resolve(value, context) as string
648
+ // Should start with function selector for transfer(address)
649
+ expect(result.startsWith('0x1a695230')).toBe(true)
650
+ // Should be 4 bytes (selector) + 32 bytes (address parameter) = 72 characters + 2 for '0x'
651
+ expect(result.length).toBe(74)
652
+ })
653
+
654
+ it('should encode a function with multiple parameters', async () => {
655
+ const value: AbiEncodeValue = {
656
+ type: 'abi-encode',
657
+ arguments: {
658
+ signature: 'transfer(address,uint256)',
659
+ values: ['0x1234567890123456789012345678901234567890', '1000000000000000000']
660
+ }
661
+ }
662
+ const result = await resolver.resolve(value, context) as string
663
+ // Should start with function selector for transfer(address,uint256)
664
+ expect(result.startsWith('0xa9059cbb')).toBe(true)
665
+ // Should be 4 bytes (selector) + 32 bytes (address) + 32 bytes (uint256) = 136 characters + 2 for '0x'
666
+ expect(result.length).toBe(138)
667
+ })
668
+
669
+ it('should encode a function with various parameter types', async () => {
670
+ const value: AbiEncodeValue = {
671
+ type: 'abi-encode',
672
+ arguments: {
673
+ signature: 'complexFunction(address,uint256,bool,string)',
674
+ values: [
675
+ '0x1234567890123456789012345678901234567890',
676
+ '42',
677
+ true,
678
+ 'hello world'
679
+ ]
680
+ }
681
+ }
682
+ const result = await resolver.resolve(value, context) as string
683
+ // Should be a valid hex string starting with 0x
684
+ expect(result.startsWith('0x')).toBe(true)
685
+ expect(result.length % 2).toBe(0) // Should be even length
686
+ // Should be longer than just the selector due to multiple parameters
687
+ expect(result.length).toBeGreaterThan(10)
688
+ })
689
+
690
+ it('should handle string parameters correctly', async () => {
691
+ const value: AbiEncodeValue = {
692
+ type: 'abi-encode',
693
+ arguments: {
694
+ signature: 'setName(string)',
695
+ values: ['Alice']
696
+ }
697
+ }
698
+ const result = await resolver.resolve(value, context) as string
699
+ expect(result.startsWith('0x')).toBe(true)
700
+ // Verify we can decode it back
701
+ const iface = new ethers.Interface(['function setName(string)'])
702
+ const decoded = iface.decodeFunctionData('setName', result)
703
+ expect(decoded[0]).toBe('Alice')
704
+ })
705
+
706
+ it('should handle array parameters', async () => {
707
+ const value: AbiEncodeValue = {
708
+ type: 'abi-encode',
709
+ arguments: {
710
+ signature: 'batchTransfer(address[])',
711
+ values: [['0x1234567890123456789012345678901234567890', '0x0987654321098765432109876543210987654321']]
712
+ }
713
+ }
714
+ const result = await resolver.resolve(value, context) as string
715
+ expect(result.startsWith('0x')).toBe(true)
716
+ // Verify we can decode it back
717
+ const iface = new ethers.Interface(['function batchTransfer(address[])'])
718
+ const decoded = iface.decodeFunctionData('batchTransfer', result)
719
+ expect(decoded[0]).toEqual(['0x1234567890123456789012345678901234567890', '0x0987654321098765432109876543210987654321'])
720
+ })
721
+
722
+ it('should validate that signature is provided', async () => {
723
+ const value = {
724
+ type: 'abi-encode' as const,
725
+ arguments: {
726
+ signature: null as any,
727
+ values: []
728
+ }
729
+ }
730
+
731
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
732
+ 'abi-encode: signature is required'
733
+ )
734
+ })
735
+
736
+ it('should validate that values array is provided', async () => {
737
+ const value = {
738
+ type: 'abi-encode' as const,
739
+ arguments: {
740
+ signature: 'transfer(address)',
741
+ values: null as any
742
+ }
743
+ }
744
+
745
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
746
+ 'abi-encode: values array is required'
747
+ )
748
+ })
749
+
750
+ it('should handle invalid function signatures gracefully', async () => {
751
+ // Mock console.log to suppress the warnings during testing
752
+ const originalConsoleLog = console.log
753
+ console.log = jest.fn()
754
+
755
+ const value: AbiEncodeValue = {
756
+ type: 'abi-encode',
757
+ arguments: {
758
+ signature: 'invalid signature format',
759
+ values: []
760
+ }
761
+ }
762
+
763
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
764
+ /abi-encode: Failed to encode function data:/
765
+ )
766
+
767
+ // Restore console.log
768
+ console.log = originalConsoleLog
769
+ })
770
+
771
+ it('should handle mismatched parameter count', async () => {
772
+ const value: AbiEncodeValue = {
773
+ type: 'abi-encode',
774
+ arguments: {
775
+ signature: 'transfer(address,uint256)',
776
+ values: ['0x1234567890123456789012345678901234567890'] // Missing the uint256 parameter
777
+ }
778
+ }
779
+
780
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
781
+ /abi-encode: Failed to encode function data:/
782
+ )
783
+ })
784
+
785
+ it('should handle type mismatches gracefully', async () => {
786
+ const value: AbiEncodeValue = {
787
+ type: 'abi-encode',
788
+ arguments: {
789
+ signature: 'transfer(address,uint256)',
790
+ values: ['not-an-address', 'not-a-number']
791
+ }
792
+ }
793
+
794
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
795
+ /abi-encode: Failed to encode function data:/
796
+ )
797
+ })
798
+ })
799
+
800
+ describe('abi-pack', () => {
801
+ it('should pack a single uint256 value', async () => {
802
+ const value: AbiPackValue = {
803
+ type: 'abi-pack',
804
+ arguments: {
805
+ types: ['uint256'],
806
+ values: ['42']
807
+ }
808
+ }
809
+ const result = await resolver.resolve(value, context) as string
810
+ // Packed uint256 should be 32 bytes without padding
811
+ expect(result).toBe('0x000000000000000000000000000000000000000000000000000000000000002a')
812
+ })
813
+
814
+ it('should pack multiple values with different types', async () => {
815
+ const value: AbiPackValue = {
816
+ type: 'abi-pack',
817
+ arguments: {
818
+ types: ['uint256', 'uint8', 'address'],
819
+ values: ['42', '255', '0x1234567890123456789012345678901234567890']
820
+ }
821
+ }
822
+ const result = await resolver.resolve(value, context) as string
823
+ // Should be packed without padding between values
824
+ expect(result.startsWith('0x')).toBe(true)
825
+ expect(result.length).toBeGreaterThan(2) // More than just '0x'
826
+ })
827
+
828
+ it('should pack string values correctly', async () => {
829
+ const value: AbiPackValue = {
830
+ type: 'abi-pack',
831
+ arguments: {
832
+ types: ['string'],
833
+ values: ['hello']
834
+ }
835
+ }
836
+ const result = await resolver.resolve(value, context) as string
837
+ expect(result.startsWith('0x')).toBe(true)
838
+ // String should be packed without length prefix (unlike ABI encoding)
839
+ expect(result).toBe('0x68656c6c6f') // 'hello' in hex
840
+ })
841
+
842
+ it('should pack bytes values correctly', async () => {
843
+ const value: AbiPackValue = {
844
+ type: 'abi-pack',
845
+ arguments: {
846
+ types: ['bytes'],
847
+ values: ['0xdeadbeef']
848
+ }
849
+ }
850
+ const result = await resolver.resolve(value, context) as string
851
+ expect(result).toBe('0xdeadbeef')
852
+ })
853
+
854
+ it('should pack uint8 values without padding', async () => {
855
+ const value: AbiPackValue = {
856
+ type: 'abi-pack',
857
+ arguments: {
858
+ types: ['uint8', 'uint8'],
859
+ values: ['255', '128']
860
+ }
861
+ }
862
+ const result = await resolver.resolve(value, context) as string
863
+ // Two uint8 values should be packed as 2 bytes total
864
+ expect(result).toBe('0xff80')
865
+ })
866
+
867
+ it('should pack address values correctly', async () => {
868
+ const value: AbiPackValue = {
869
+ type: 'abi-pack',
870
+ arguments: {
871
+ types: ['address'],
872
+ values: ['0x1234567890123456789012345678901234567890']
873
+ }
874
+ }
875
+ const result = await resolver.resolve(value, context) as string
876
+ // Address should be packed as 20 bytes
877
+ expect(result).toBe('0x1234567890123456789012345678901234567890')
878
+ })
879
+
880
+ it('should pack boolean values correctly', async () => {
881
+ const value: AbiPackValue = {
882
+ type: 'abi-pack',
883
+ arguments: {
884
+ types: ['bool', 'bool'],
885
+ values: [true, false]
886
+ }
887
+ }
888
+ const result = await resolver.resolve(value, context) as string
889
+ // Booleans should be packed as single bytes
890
+ expect(result).toBe('0x0100')
891
+ })
892
+
893
+ it('should pack mixed types in correct order', async () => {
894
+ const value: AbiPackValue = {
895
+ type: 'abi-pack',
896
+ arguments: {
897
+ types: ['uint8', 'address', 'uint8'],
898
+ values: ['42', '0x1234567890123456789012345678901234567890', '255']
899
+ }
900
+ }
901
+ const result = await resolver.resolve(value, context) as string
902
+ // Should be: 1 byte (uint8) + 20 bytes (address) + 1 byte (uint8) = 22 bytes = 44 hex chars + 2 for '0x'
903
+ expect(result.length).toBe(46)
904
+ expect(result.startsWith('0x2a')).toBe(true) // First byte should be 42 (0x2a)
905
+ expect(result.endsWith('ff')).toBe(true) // Last byte should be 255 (0xff)
906
+ })
907
+
908
+ it('should resolve values from context before packing', async () => {
909
+ context.setOutput('myValue', '100')
910
+ context.setOutput('myAddress', '0x1234567890123456789012345678901234567890')
911
+
912
+ const value: AbiPackValue = {
913
+ type: 'abi-pack',
914
+ arguments: {
915
+ types: ['uint256', 'address'],
916
+ values: ['{{myValue}}', '{{myAddress}}']
917
+ }
918
+ }
919
+ const result = await resolver.resolve(value, context) as string
920
+ expect(result.startsWith('0x')).toBe(true)
921
+ // Should contain the resolved values
922
+ expect(result.length).toBeGreaterThan(2)
923
+ })
924
+
925
+ it('should validate that types array is provided', async () => {
926
+ const value = {
927
+ type: 'abi-pack' as const,
928
+ arguments: {
929
+ types: null as any,
930
+ values: ['42']
931
+ }
932
+ }
933
+
934
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
935
+ 'abi-pack: types array is required'
936
+ )
937
+ })
938
+
939
+ it('should validate that values array is provided', async () => {
940
+ const value = {
941
+ type: 'abi-pack' as const,
942
+ arguments: {
943
+ types: ['uint256'],
944
+ values: null as any
945
+ }
946
+ }
947
+
948
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
949
+ 'abi-pack: values array is required'
950
+ )
951
+ })
952
+
953
+ it('should validate that types and values arrays have same length', async () => {
954
+ const value: AbiPackValue = {
955
+ type: 'abi-pack',
956
+ arguments: {
957
+ types: ['uint256', 'uint8'],
958
+ values: ['42'] // Missing second value
959
+ }
960
+ }
961
+
962
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
963
+ 'abi-pack: types array length (2) must match values array length (1)'
964
+ )
965
+ })
966
+
967
+ it('should validate that all types are strings', async () => {
968
+ const value = {
969
+ type: 'abi-pack' as const,
970
+ arguments: {
971
+ types: ['uint256', 123 as any], // Invalid type
972
+ values: ['42', '255']
973
+ }
974
+ }
975
+
976
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
977
+ 'abi-pack: all types must be strings'
978
+ )
979
+ })
980
+
981
+ it('should handle invalid type gracefully', async () => {
982
+ const value: AbiPackValue = {
983
+ type: 'abi-pack',
984
+ arguments: {
985
+ types: ['invalidType'],
986
+ values: ['42']
987
+ }
988
+ }
989
+
990
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
991
+ /abi-pack: Failed to pack values:/
992
+ )
993
+ })
994
+
995
+ it('should handle type mismatches gracefully', async () => {
996
+ const value: AbiPackValue = {
997
+ type: 'abi-pack',
998
+ arguments: {
999
+ types: ['uint256'],
1000
+ values: ['not-a-number']
1001
+ }
1002
+ }
1003
+
1004
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
1005
+ /abi-pack: Failed to pack values:/
1006
+ )
1007
+ })
1008
+
1009
+ it('should handle empty arrays', async () => {
1010
+ const value: AbiPackValue = {
1011
+ type: 'abi-pack',
1012
+ arguments: {
1013
+ types: [],
1014
+ values: []
1015
+ }
1016
+ }
1017
+ const result = await resolver.resolve(value, context) as string
1018
+ expect(result).toBe('0x')
1019
+ })
1020
+
1021
+ it('should pack large numbers correctly', async () => {
1022
+ const value: AbiPackValue = {
1023
+ type: 'abi-pack',
1024
+ arguments: {
1025
+ types: ['uint256'],
1026
+ values: ['115792089237316195423570985008687907853269984665640564039457584007913129639935'] // max uint256
1027
+ }
1028
+ }
1029
+ const result = await resolver.resolve(value, context) as string
1030
+ expect(result).toBe('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
1031
+ })
1032
+
1033
+ it('should demonstrate difference from abi-encode', async () => {
1034
+ // Compare abi-pack vs abi-encode for the same data
1035
+ const packValue: AbiPackValue = {
1036
+ type: 'abi-pack',
1037
+ arguments: {
1038
+ types: ['uint8', 'uint8'],
1039
+ values: ['42', '255']
1040
+ }
1041
+ }
1042
+
1043
+ const encodeValue: AbiEncodeValue = {
1044
+ type: 'abi-encode',
1045
+ arguments: {
1046
+ signature: 'test(uint8,uint8)',
1047
+ values: ['42', '255']
1048
+ }
1049
+ }
1050
+
1051
+ const packResult = await resolver.resolve(packValue, context) as string
1052
+ const encodeResult = await resolver.resolve(encodeValue, context) as string
1053
+
1054
+ // Packed should be much shorter (no padding, no function selector)
1055
+ expect(packResult).toBe('0x2aff') // Just 2 bytes
1056
+ expect(encodeResult.length).toBeGreaterThan(packResult.length) // ABI encode includes function selector and padding
1057
+ })
1058
+ })
1059
+
1060
+ describe('artifact function expressions', () => {
1061
+ beforeEach(() => {
1062
+ // Add test artifacts to the registry
1063
+ const testArtifact1 = {
1064
+ contractName: 'TestContract',
1065
+ abi: [{"type":"function","name":"test","inputs":[],"outputs":[{"type":"uint256"}]}],
1066
+ bytecode: '0x608060405234801561000f575f5ffd5b50602a5f526020601ff3',
1067
+ _path: '/test/TestContract.json',
1068
+ _hash: 'abc123'
1069
+ }
1070
+
1071
+ const testArtifact2 = {
1072
+ contractName: 'ContractWithoutBytecode',
1073
+ abi: [{"type":"function","name":"example","inputs":[],"outputs":[]}],
1074
+ bytecode: '', // Empty bytecode
1075
+ _path: '/test/ContractWithoutBytecode.json',
1076
+ _hash: 'def456'
1077
+ }
1078
+
1079
+ const testArtifact3 = {
1080
+ contractName: 'ContractWithoutABI',
1081
+ abi: [], // Empty ABI
1082
+ bytecode: '0x608060405234801561000f575f5ffd5b50602a5f526020601ff4', // Different bytecode
1083
+ _path: '/test/ContractWithoutABI.json',
1084
+ _hash: 'ghi789'
1085
+ }
1086
+
1087
+ mockRegistry.addForTesting(testArtifact1)
1088
+ mockRegistry.addForTesting(testArtifact2)
1089
+ mockRegistry.addForTesting(testArtifact3)
1090
+ })
1091
+
1092
+ describe('Contract(...).creationCode', () => {
1093
+ it('should return bytecode for valid artifact', async () => {
1094
+ const result = await resolver.resolve('{{Contract(TestContract).creationCode}}', context)
1095
+ expect(result).toBe('0x608060405234801561000f575f5ffd5b50602a5f526020601ff3')
1096
+ })
1097
+
1098
+ it('should return bytecode for valid artifact using initCode alias', async () => {
1099
+ const result = await resolver.resolve('{{Contract(TestContract).creationCode}}', context)
1100
+ expect(result).toBe('0x608060405234801561000f575f5ffd5b50602a5f526020601ff3')
1101
+ })
1102
+
1103
+ it('should resolve nested in value resolver objects', async () => {
1104
+ const value = {
1105
+ type: 'constructor-encode' as const,
1106
+ arguments: {
1107
+ creationCode: '{{Contract(TestContract).creationCode}}',
1108
+ types: [],
1109
+ values: []
1110
+ }
1111
+ }
1112
+ const result = await resolver.resolve(value, context)
1113
+ expect(result).toBe('0x608060405234801561000f575f5ffd5b50602a5f526020601ff3')
1114
+ })
1115
+
1116
+ it('should throw error for non-existent artifact', async () => {
1117
+ await expect(resolver.resolve('{{Contract(NonExistent).creationCode}}', context))
1118
+ .rejects.toThrow('Artifact not found for reference: "NonExistent"')
1119
+ })
1120
+
1121
+ it('should throw error for artifact missing bytecode', async () => {
1122
+ // Create an artifact without bytecode (empty string)
1123
+ const artifactWithoutBytecode = {
1124
+ contractName: 'ContractWithoutBytecode',
1125
+ abi: [],
1126
+ bytecode: '',
1127
+ _path: '/test/ContractWithoutBytecode.json',
1128
+ _hash: 'empty123'
1129
+ }
1130
+ mockRegistry.addForTesting(artifactWithoutBytecode)
1131
+
1132
+ const contract = mockRegistry.lookup('ContractWithoutBytecode')
1133
+ expect(contract?.creationCode).toBe('')
1134
+
1135
+ // When creationCode is empty, accessing it should still return the empty string
1136
+ const result = await resolver.resolve('{{Contract(ContractWithoutBytecode).creationCode}}', context)
1137
+ expect(result).toBe('')
1138
+ })
1139
+
1140
+ it('should handle artifacts with null bytecode', async () => {
1141
+ const artifactWithNullBytecode = {
1142
+ contractName: 'NullBytecodeContract',
1143
+ abi: [],
1144
+ bytecode: null as any,
1145
+ _path: '/test/NullBytecodeContract.json',
1146
+ _hash: 'null123'
1147
+ }
1148
+
1149
+ // Should fail to add to registry due to missing bytecode
1150
+ expect(() => mockRegistry.addForTesting(artifactWithNullBytecode))
1151
+ .toThrow('Cannot hydrate contract from /test/NullBytecodeContract.json: missing creation code')
1152
+ })
1153
+
1154
+ it('should handle artifacts with undefined bytecode', async () => {
1155
+ const artifactWithUndefinedBytecode = {
1156
+ contractName: 'UndefinedBytecodeContract',
1157
+ abi: [],
1158
+ bytecode: undefined as any,
1159
+ _path: '/test/UndefinedBytecodeContract.json',
1160
+ _hash: 'undef123'
1161
+ }
1162
+
1163
+ // Should fail to add to registry due to missing bytecode
1164
+ expect(() => mockRegistry.addForTesting(artifactWithUndefinedBytecode))
1165
+ .toThrow('Cannot hydrate contract from /test/UndefinedBytecodeContract.json: missing creation code')
1166
+ })
1167
+ })
1168
+
1169
+ describe('Contract(...).abi', () => {
1170
+ it('should return abi for valid artifact', async () => {
1171
+ const result = await resolver.resolve('{{Contract(TestContract).abi}}', context)
1172
+ expect(result).toEqual([{"type":"function","name":"test","inputs":[],"outputs":[{"type":"uint256"}]}])
1173
+ })
1174
+
1175
+ it('should resolve nested in value resolver objects', async () => {
1176
+ // This would be used in a context where ABI is needed for encoding/decoding
1177
+ const abiValue = await resolver.resolve('{{Contract(TestContract).abi}}', context)
1178
+ context.setOutput('contractAbi', abiValue)
1179
+ const resolvedAbi = await resolver.resolve('{{contractAbi}}', context)
1180
+ expect(resolvedAbi).toEqual([{"type":"function","name":"test","inputs":[],"outputs":[{"type":"uint256"}]}])
1181
+ })
1182
+
1183
+ it('should throw error for non-existent artifact', async () => {
1184
+ await expect(resolver.resolve('{{Contract(NonExistent).abi}}', context))
1185
+ .rejects.toThrow('Artifact not found for reference: "NonExistent"')
1186
+ })
1187
+
1188
+ it('should return empty abi for artifact with empty abi array', async () => {
1189
+ // Use the ContractWithoutABI that's already registered in beforeEach
1190
+ const result = await resolver.resolve('{{Contract(ContractWithoutABI).abi}}', context)
1191
+ expect(result).toEqual([])
1192
+ })
1193
+
1194
+ it('should handle artifacts with null abi', async () => {
1195
+ const artifactWithNullAbi = {
1196
+ contractName: 'NullAbiContract',
1197
+ abi: null as any,
1198
+ bytecode: '0x123',
1199
+ _path: '/test/NullAbiContract.json',
1200
+ _hash: 'nullabi123'
1201
+ }
1202
+ mockRegistry.addForTesting(artifactWithNullAbi)
1203
+
1204
+ await expect(resolver.resolve('{{Contract(NullAbiContract).abi}}', context))
1205
+ .rejects.toThrow('Property "abi" does not exist on contract found for reference "NullAbiContract"')
1206
+ })
1207
+
1208
+ it('should handle artifacts with undefined abi', async () => {
1209
+ const artifactWithUndefinedAbi = {
1210
+ contractName: 'UndefinedAbiContract',
1211
+ abi: undefined as any,
1212
+ bytecode: '0x123',
1213
+ _path: '/test/UndefinedAbiContract.json',
1214
+ _hash: 'undefabi123'
1215
+ }
1216
+ mockRegistry.addForTesting(artifactWithUndefinedAbi)
1217
+
1218
+ await expect(resolver.resolve('{{Contract(UndefinedAbiContract).abi}}', context))
1219
+ .rejects.toThrow('Property "abi" does not exist on contract found for reference "UndefinedAbiContract"')
1220
+ })
1221
+ })
1222
+
1223
+ describe('Contract(...).buildInfoId', () => {
1224
+ it('should return buildInfoId for contract hydrated from build-info file', async () => {
1225
+ // Add a contract that was hydrated from a build-info file
1226
+ const buildInfoArtifact = {
1227
+ contractName: 'TestBuildInfoContract',
1228
+ abi: [{"type":"function","name":"test","inputs":[],"outputs":[{"type":"uint256"}]}],
1229
+ bytecode: '0x608060405234801561000f575f5ffd5b50602a5f526020601ff3',
1230
+ sourceName: 'src/TestContract.sol',
1231
+ compiler: { name: 'solc', version: '0.8.19' },
1232
+ buildInfoId: 'src/TestContract.sol:TestContract',
1233
+ _path: '/test/build-info/test.json',
1234
+ _hash: 'buildinfo123'
1235
+ }
1236
+ mockRegistry.addForTesting(buildInfoArtifact)
1237
+
1238
+ const result = await resolver.resolve('{{Contract(TestBuildInfoContract).buildInfoId}}', context)
1239
+ expect(result).toBe('src/TestContract.sol:TestContract')
1240
+ })
1241
+
1242
+ it('should throw error for non-existent artifact', async () => {
1243
+ await expect(resolver.resolve('{{Contract(NonExistent).buildInfoId}}', context))
1244
+ .rejects.toThrow('Artifact not found for reference: "NonExistent"')
1245
+ })
1246
+
1247
+ it('should throw error for contract that was not hydrated from build-info file', async () => {
1248
+ // Use the existing TestContract which should not have buildInfoId
1249
+ await expect(resolver.resolve('{{Contract(TestContract).buildInfoId}}', context))
1250
+ .rejects.toThrow('Property "buildInfoId" does not exist on contract found for reference "TestContract"')
1251
+ })
1252
+ })
1253
+
1254
+ describe('invalid Contract expressions', () => {
1255
+ it('should throw error for unknown function names', async () => {
1256
+ await expect(resolver.resolve('{{unknownFunction(TestContract)}}', context))
1257
+ .rejects.toThrow('Failed to resolve expression "{{unknownFunction(TestContract)}}"')
1258
+ })
1259
+
1260
+ it('should throw error for malformed expressions', async () => {
1261
+ await expect(resolver.resolve('{{creationCode}}', context))
1262
+ .rejects.toThrow('Failed to resolve expression "{{creationCode}}"')
1263
+ })
1264
+
1265
+ it('should throw error for invalid syntax', async () => {
1266
+ await expect(resolver.resolve('{{creationCode TestContract}}', context))
1267
+ .rejects.toThrow('Failed to resolve expression "{{creationCode TestContract}}"')
1268
+ })
1269
+
1270
+ it('should throw error for empty function calls', async () => {
1271
+ // Empty string should not match any artifact and should throw an error
1272
+ await expect(resolver.resolve('{{Contract().creationCode}}', context)).rejects.toThrow(
1273
+ 'Artifact not found for reference: ""'
1274
+ )
1275
+ })
1276
+
1277
+ it('should throw error for function calls with whitespace only', async () => {
1278
+ // Whitespace-only string gets trimmed to empty and should throw an error
1279
+ await expect(resolver.resolve('{{Contract( ).creationCode}}', context)).rejects.toThrow(
1280
+ 'Artifact not found for reference: ""'
1281
+ )
1282
+ })
1283
+
1284
+ it('should handle function calls with extra whitespace in argument', async () => {
1285
+ const result = await resolver.resolve('{{Contract( TestContract ).creationCode}}', context)
1286
+ expect(result).toBe('0x608060405234801561000f575f5ffd5b50602a5f526020601ff3')
1287
+ })
1288
+
1289
+ it('should handle mixed case function names correctly', async () => {
1290
+ await expect(resolver.resolve('{{CreationCode(TestContract)}}', context))
1291
+ .rejects.toThrow('Failed to resolve expression "{{CreationCode(TestContract)}}"')
1292
+ })
1293
+
1294
+ it('should handle function calls with multiple arguments (should fail)', async () => {
1295
+ await expect(resolver.resolve('{{Contract(TestContract, ExtraArg).creationCode}}', context))
1296
+ .rejects.toThrow('Artifact not found for reference: "TestContract, ExtraArg"')
1297
+ })
1298
+ })
1299
+
1300
+ describe('edge cases', () => {
1301
+ it('should handle artifacts identified by contract name', async () => {
1302
+ // Add an artifact that can be found by contract name
1303
+ const pathArtifact = {
1304
+ contractName: 'PathContract',
1305
+ abi: [],
1306
+ bytecode: '0xabcdef',
1307
+ _path: '/very/long/path/to/contracts/PathContract.json',
1308
+ _hash: 'path123'
1309
+ }
1310
+ mockRegistry.addForTesting(pathArtifact)
1311
+
1312
+ const result = await resolver.resolve('{{Contract(PathContract).creationCode}}', context)
1313
+ expect(result).toBe('0xabcdef')
1314
+ })
1315
+
1316
+ it('should handle artifacts with special characters in names', async () => {
1317
+ const specialArtifact = {
1318
+ contractName: 'Special-Contract_v2',
1319
+ abi: [],
1320
+ bytecode: '0xspecial',
1321
+ _path: '/test/Special-Contract_v2.json',
1322
+ _hash: 'special123'
1323
+ }
1324
+ mockRegistry.addForTesting(specialArtifact)
1325
+
1326
+ const result = await resolver.resolve('{{Contract(Special-Contract_v2).creationCode}}', context)
1327
+ expect(result).toBe('0xspecial')
1328
+ })
1329
+
1330
+ it('should be case sensitive for artifact names', async () => {
1331
+ await expect(resolver.resolve('{{Contract(testcontract).creationCode}}', context))
1332
+ .rejects.toThrow('Artifact not found for reference: "testcontract"')
1333
+ })
1334
+
1335
+ it('should handle resolution with context variables for artifact names', async () => {
1336
+ context.setOutput('contractName', 'TestContract')
1337
+ const contractName = await resolver.resolve('{{contractName}}', context)
1338
+ const result = await resolver.resolve(`{{Contract(${contractName}).creationCode}}`, context)
1339
+ expect(result).toBe('0x608060405234801561000f575f5ffd5b50602a5f526020601ff3')
1340
+ })
1341
+ })
1342
+ })
1343
+
1344
+ describe('call', () => {
1345
+ let testContractAddress: string
1346
+ let anvilProvider: ethers.JsonRpcProvider
1347
+
1348
+ beforeEach(async () => {
1349
+ anvilProvider = context.provider as ethers.JsonRpcProvider
1350
+
1351
+ // Set the Mini contract bytecode directly to an address using anvil_setCode
1352
+ // This contract has:
1353
+ // - test() public returns (uint256) -> returns 42
1354
+ // - multiply2numbers(uint256 a, uint256 b) public returns (uint256) -> returns a * b
1355
+ testContractAddress = '0x5FbDB2315678afecb367f032d93F642f64180aa3' // Standard first contract address
1356
+ const miniContractBytecode = '0x608060405234801561000f575f5ffd5b5060043610610034575f3560e01c80636df5b97a14610038578063f8a8fd6d14610068575b5f5ffd5b610052600480360381019061004d91906100da565b610086565b60405161005f9190610127565b60405180910390f35b61007061009b565b60405161007d9190610127565b60405180910390f35b5f8183610093919061016d565b905092915050565b5f602a905090565b5f5ffd5b5f819050919050565b6100b9816100a7565b81146100c3575f5ffd5b50565b5f813590506100d4816100b0565b92915050565b5f5f604083850312156100f0576100ef6100a3565b5b5f6100fd858286016100c6565b925050602061010e858286016100c6565b9150509250929050565b610121816100a7565b82525050565b5f60208201905061013a5f830184610118565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610177826100a7565b9150610182836100a7565b9250828202610190816100a7565b915082820484148315176101a7576101a6610140565b5b509291505056fea264697066735822122071d40daa3d2beacd91f29d29ccf1c0b6f312e805f50b37166267c0a2a55e6e6164736f6c634300081c0033'
1357
+
1358
+ // Use anvil_setCode to set the deployed bytecode directly at the address
1359
+ await anvilProvider.send('anvil_setCode', [testContractAddress, miniContractBytecode])
1360
+ })
1361
+
1362
+ it('should call test() function and return 42', async () => {
1363
+ const value: CallValue = {
1364
+ type: 'call',
1365
+ arguments: {
1366
+ to: testContractAddress,
1367
+ signature: 'test() returns (uint256)',
1368
+ values: []
1369
+ }
1370
+ }
1371
+
1372
+ const result = await resolver.resolve(value, context)
1373
+ expect(result).toBe(42n) // ethers returns BigInt for uint256
1374
+ })
1375
+
1376
+ it('should call multiply2numbers with parameters', async () => {
1377
+ const value: CallValue = {
1378
+ type: 'call',
1379
+ arguments: {
1380
+ to: testContractAddress,
1381
+ signature: 'multiply2numbers(uint256,uint256) returns (uint256)',
1382
+ values: ['7', '6']
1383
+ }
1384
+ }
1385
+
1386
+ const result = await resolver.resolve(value, context)
1387
+ expect(result).toBe(42n) // 7 * 6 = 42
1388
+ })
1389
+
1390
+ it('should resolve address from context variable', async () => {
1391
+ context.setOutput('contractAddr', testContractAddress)
1392
+
1393
+ const value: CallValue = {
1394
+ type: 'call',
1395
+ arguments: {
1396
+ to: '{{contractAddr}}',
1397
+ signature: 'test() returns (uint256)',
1398
+ values: []
1399
+ }
1400
+ }
1401
+
1402
+ const result = await resolver.resolve(value, context)
1403
+ expect(result).toBe(42n)
1404
+ })
1405
+
1406
+ it('should resolve parameters from context variables', async () => {
1407
+ context.setOutput('firstNumber', '15')
1408
+ context.setOutput('secondNumber', '25')
1409
+
1410
+ const value: CallValue = {
1411
+ type: 'call',
1412
+ arguments: {
1413
+ to: testContractAddress,
1414
+ signature: 'multiply2numbers(uint256,uint256) returns (uint256)',
1415
+ values: ['{{firstNumber}}', '{{secondNumber}}']
1416
+ }
1417
+ }
1418
+
1419
+ const result = await resolver.resolve(value, context)
1420
+ expect(result).toBe(375n) // 15 * 25 = 375
1421
+ })
1422
+
1423
+ it('should handle large number multiplication', async () => {
1424
+ const value: CallValue = {
1425
+ type: 'call',
1426
+ arguments: {
1427
+ to: testContractAddress,
1428
+ signature: 'multiply2numbers(uint256,uint256) returns (uint256)',
1429
+ values: ['1000000000000000000', '2000000000000000000'] // 1e18 * 2e18
1430
+ }
1431
+ }
1432
+
1433
+ const result = await resolver.resolve(value, context)
1434
+ expect(result).toBe(2000000000000000000000000000000000000n) // 2e36
1435
+ })
1436
+
1437
+ it('should throw error when calling non-existent function on deployed contract', async () => {
1438
+ const value: CallValue = {
1439
+ type: 'call',
1440
+ arguments: {
1441
+ to: testContractAddress,
1442
+ signature: 'nonExistentFunction() view returns (uint256)',
1443
+ values: []
1444
+ }
1445
+ }
1446
+
1447
+ // This should fail because the function doesn't exist on the contract
1448
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
1449
+ /call: Failed to execute contract call:/
1450
+ )
1451
+ })
1452
+
1453
+ it('should throw error for mismatched parameter count', async () => {
1454
+ const value: CallValue = {
1455
+ type: 'call',
1456
+ arguments: {
1457
+ to: testContractAddress,
1458
+ signature: 'multiply2numbers(uint256,uint256) returns (uint256)',
1459
+ values: ['10'] // Missing the second parameter
1460
+ }
1461
+ }
1462
+
1463
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
1464
+ /call: Failed to execute contract call:/
1465
+ )
1466
+ })
1467
+
1468
+ it('should handle type mismatches gracefully', async () => {
1469
+ const value: CallValue = {
1470
+ type: 'call',
1471
+ arguments: {
1472
+ to: testContractAddress,
1473
+ signature: 'multiply2numbers(uint256,uint256) returns (uint256)',
1474
+ values: ['not-a-number', 'also-not-a-number']
1475
+ }
1476
+ }
1477
+
1478
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
1479
+ /call: Failed to execute contract call:/
1480
+ )
1481
+ })
1482
+
1483
+ it('should throw error when no target address provided', async () => {
1484
+ const value: CallValue = {
1485
+ type: 'call',
1486
+ arguments: {
1487
+ signature: 'test() returns (uint256)',
1488
+ values: []
1489
+ }
1490
+ }
1491
+
1492
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
1493
+ 'call: target address (to) is required'
1494
+ )
1495
+ })
1496
+
1497
+ it('should throw error for invalid target address', async () => {
1498
+ const value: CallValue = {
1499
+ type: 'call',
1500
+ arguments: {
1501
+ to: 'invalid-address',
1502
+ signature: 'test() returns (uint256)',
1503
+ values: []
1504
+ }
1505
+ }
1506
+
1507
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
1508
+ 'call: invalid target address: invalid-address'
1509
+ )
1510
+ })
1511
+
1512
+ it('should throw error when no signature provided', async () => {
1513
+ const value: CallValue = {
1514
+ type: 'call',
1515
+ arguments: {
1516
+ to: testContractAddress,
1517
+ signature: null as any,
1518
+ values: []
1519
+ }
1520
+ }
1521
+
1522
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
1523
+ 'call: function signature is required'
1524
+ )
1525
+ })
1526
+
1527
+ it('should throw error when no values array provided', async () => {
1528
+ const value: CallValue = {
1529
+ type: 'call',
1530
+ arguments: {
1531
+ to: testContractAddress,
1532
+ signature: 'test() returns (uint256)',
1533
+ values: null as any
1534
+ }
1535
+ }
1536
+
1537
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
1538
+ 'call: values array is required'
1539
+ )
1540
+ })
1541
+
1542
+ it('should throw error for invalid function signature', async () => {
1543
+ // Mock console.log to suppress the warnings during testing
1544
+ const originalConsoleLog = console.log
1545
+ console.log = jest.fn()
1546
+
1547
+ const value: CallValue = {
1548
+ type: 'call',
1549
+ arguments: {
1550
+ to: testContractAddress,
1551
+ signature: 'invalid signature format',
1552
+ values: []
1553
+ }
1554
+ }
1555
+
1556
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
1557
+ /call: Failed to execute contract call:/
1558
+ )
1559
+
1560
+ // Restore console.log
1561
+ console.log = originalConsoleLog
1562
+ })
1563
+
1564
+ it('should handle null address gracefully', async () => {
1565
+ const value: CallValue = {
1566
+ type: 'call',
1567
+ arguments: {
1568
+ to: null as any,
1569
+ signature: 'test() returns (uint256)',
1570
+ values: []
1571
+ }
1572
+ }
1573
+
1574
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
1575
+ 'call: target address (to) is required'
1576
+ )
1577
+ })
1578
+
1579
+ it('should handle undefined address gracefully', async () => {
1580
+ const value: CallValue = {
1581
+ type: 'call',
1582
+ arguments: {
1583
+ to: undefined as any,
1584
+ signature: 'test() returns (uint256)',
1585
+ values: []
1586
+ }
1587
+ }
1588
+
1589
+ await expect(resolver.resolve(value, context)).rejects.toThrow(
1590
+ 'call: target address (to) is required'
1591
+ )
1592
+ })
1593
+
1594
+ it('should work with zero parameters', async () => {
1595
+ const value: CallValue = {
1596
+ type: 'call',
1597
+ arguments: {
1598
+ to: testContractAddress,
1599
+ signature: 'test() returns (uint256)',
1600
+ values: []
1601
+ }
1602
+ }
1603
+
1604
+ const result = await resolver.resolve(value, context)
1605
+ expect(result).toBe(42n)
1606
+ })
1607
+
1608
+ it('should work with string parameters converted to numbers', async () => {
1609
+ const value: CallValue = {
1610
+ type: 'call',
1611
+ arguments: {
1612
+ to: testContractAddress,
1613
+ signature: 'multiply2numbers(uint256,uint256) returns (uint256)',
1614
+ values: ['100', '200']
1615
+ }
1616
+ }
1617
+
1618
+ const result = await resolver.resolve(value, context)
1619
+ expect(result).toBe(20000n)
1620
+ })
1621
+ })
1622
+
1623
+ describe('contract-exists', () => {
1624
+ let testContractAddress: string
1625
+ let anvilProvider: ethers.JsonRpcProvider
1626
+
1627
+ beforeEach(async () => {
1628
+ anvilProvider = context.provider as ethers.JsonRpcProvider
1629
+
1630
+ // Set the Mini contract bytecode directly to an address using anvil_setCode
1631
+ // This contract has:
1632
+ // - test() public returns (uint256) -> returns 42
1633
+ // - multiply2numbers(uint256 a, uint256 b) public returns (uint256) -> returns a * b
1634
+ testContractAddress = '0x5FbDB2315678afecb367f032d93F642f64180aa3' // Standard first contract address
1635
+ const miniContractBytecode = '0x608060405234801561000f575f5ffd5b5060043610610034575f3560e01c80636df5b97a14610038578063f8a8fd6d14610068575b5f5ffd5b610052600480360381019061004d91906100da565b610086565b60405161005f9190610127565b60405180910390f35b61007061009b565b60405161007d9190610127565b60405180910390f35b5f8183610093919061016d565b905092915050565b5f602a905090565b5f5ffd5b5f819050919050565b6100b9816100a7565b81146100c3575f5ffd5b50565b5f813590506100d4816100b0565b92915050565b5f5f604083850312156100f0576100ef6100a3565b5b5f6100fd858286016100c6565b925050602061010e858286016100c6565b9150509250929050565b610121816100a7565b82525050565b5f60208201905061013a5f830184610118565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610177826100a7565b9150610182836100a7565b9250828202610190816100a7565b915082820484148315176101a7576101a6610140565b5b509291505056fea264697066735822122071d40daa3d2beacd91f29d29ccf1c0b6f312e805f50b37166267c0a2a55e6e6164736f6c634300081c0033'
1636
+
1637
+ // Use anvil_setCode to set the deployed bytecode directly at the address
1638
+ await anvilProvider.send('anvil_setCode', [testContractAddress, miniContractBytecode])
1639
+ })
1640
+
1641
+ it('should return true if contract exists', async () => {
1642
+ const value: ContractExistsValue = {
1643
+ type: 'contract-exists',
1644
+ arguments: {
1645
+ address: testContractAddress,
1646
+ },
1647
+ }
1648
+ const result = await resolver.resolve(value, context)
1649
+ expect(result).toBe(true)
1650
+ })
1651
+
1652
+ it('should return false if contract does not exist', async () => {
1653
+ const value: ContractExistsValue = {
1654
+ type: 'contract-exists',
1655
+ arguments: {
1656
+ address: '0x0000000000000000000000000000000000000001', // A non-existent address
1657
+ },
1658
+ }
1659
+ const result = await resolver.resolve(value, context)
1660
+ expect(result).toBe(false)
1661
+ })
1662
+
1663
+ it('should throw error for null address', async () => {
1664
+ const value: ContractExistsValue = {
1665
+ type: 'contract-exists',
1666
+ arguments: {
1667
+ address: null as any,
1668
+ },
1669
+ }
1670
+ await expect(resolver.resolve(value, context)).rejects.toThrow('contract-exists: invalid address: null')
1671
+ })
1672
+
1673
+ it('should throw error for undefined address', async () => {
1674
+ const value: ContractExistsValue = {
1675
+ type: 'contract-exists',
1676
+ arguments: {
1677
+ address: undefined as any,
1678
+ },
1679
+ }
1680
+ await expect(resolver.resolve(value, context)).rejects.toThrow('contract-exists: invalid address: undefined')
1681
+ })
1682
+
1683
+ it('should throw error for invalid address', async () => {
1684
+ const value: ContractExistsValue = {
1685
+ type: 'contract-exists',
1686
+ arguments: {
1687
+ address: 'invalid-address',
1688
+ },
1689
+ }
1690
+ await expect(resolver.resolve(value, context)).rejects.toThrow('contract-exists: invalid address: invalid-address')
1691
+ })
1692
+ })
1693
+ })