@0xsequence/catapult 1.4.0 → 1.5.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 (163) hide show
  1. package/README.md +27 -0
  2. package/dist/lib/__tests__/network-loader.spec.js.map +1 -1
  3. package/dist/lib/core/__tests__/resolver.spec.js +22 -0
  4. package/dist/lib/core/__tests__/resolver.spec.js.map +1 -1
  5. package/dist/lib/core/__tests__/sign-actions.spec.d.ts +2 -0
  6. package/dist/lib/core/__tests__/sign-actions.spec.d.ts.map +1 -0
  7. package/dist/lib/core/__tests__/sign-actions.spec.js +128 -0
  8. package/dist/lib/core/__tests__/sign-actions.spec.js.map +1 -0
  9. package/dist/lib/core/__tests__/signer.spec.d.ts +2 -0
  10. package/dist/lib/core/__tests__/signer.spec.d.ts.map +1 -0
  11. package/dist/lib/core/__tests__/signer.spec.js +40 -0
  12. package/dist/lib/core/__tests__/signer.spec.js.map +1 -0
  13. package/dist/lib/core/context.d.ts +3 -2
  14. package/dist/lib/core/context.d.ts.map +1 -1
  15. package/dist/lib/core/context.js +3 -2
  16. package/dist/lib/core/context.js.map +1 -1
  17. package/dist/lib/core/engine.d.ts +4 -0
  18. package/dist/lib/core/engine.d.ts.map +1 -1
  19. package/dist/lib/core/engine.js +173 -0
  20. package/dist/lib/core/engine.js.map +1 -1
  21. package/dist/lib/core/signer.d.ts +7 -0
  22. package/dist/lib/core/signer.d.ts.map +1 -0
  23. package/dist/lib/core/signer.js +60 -0
  24. package/dist/lib/core/signer.js.map +1 -0
  25. package/dist/lib/parsers/__tests__/source.spec.js +37 -0
  26. package/dist/lib/parsers/__tests__/source.spec.js.map +1 -1
  27. package/dist/lib/parsers/source.js +1 -1
  28. package/dist/lib/parsers/source.js.map +1 -1
  29. package/dist/lib/provenance.js +51 -2
  30. package/dist/lib/provenance.js.map +1 -1
  31. package/dist/lib/types/actions.d.ts +26 -2
  32. package/dist/lib/types/actions.d.ts.map +1 -1
  33. package/dist/lib/types/actions.js +3 -0
  34. package/dist/lib/types/actions.js.map +1 -1
  35. package/dist/lib/types/source.d.ts +2 -0
  36. package/dist/lib/types/source.d.ts.map +1 -1
  37. package/package.json +4 -1
  38. package/.eslintrc.json +0 -29
  39. package/.github/workflows/ci.yml +0 -181
  40. package/CONCEPT.md +0 -24
  41. package/contracts/checked-call.huff +0 -65
  42. package/eslint.config.js +0 -48
  43. package/examples/jobs/guards-v1.yaml +0 -17
  44. package/examples/jobs/sequence-seq-0001-patch.yaml +0 -59
  45. package/examples/jobs/sequence-v1.yaml +0 -59
  46. package/examples/templates/sequence-factory-v1.yaml +0 -56
  47. package/jest.config.js +0 -25
  48. package/src/cli.ts +0 -18
  49. package/src/commands/common.ts +0 -61
  50. package/src/commands/dry.ts +0 -209
  51. package/src/commands/etherscan.ts +0 -360
  52. package/src/commands/index.ts +0 -6
  53. package/src/commands/list.ts +0 -262
  54. package/src/commands/provenance.ts +0 -120
  55. package/src/commands/run.ts +0 -146
  56. package/src/commands/utils.ts +0 -215
  57. package/src/index.ts +0 -67
  58. package/src/lib/__tests__/deployer-events.spec.ts +0 -338
  59. package/src/lib/__tests__/deployer.spec.ts +0 -2269
  60. package/src/lib/__tests__/network-loader.spec.ts +0 -150
  61. package/src/lib/__tests__/network-selection.spec.ts +0 -41
  62. package/src/lib/__tests__/network-utils.spec.ts +0 -230
  63. package/src/lib/__tests__/provenance.spec.ts +0 -208
  64. package/src/lib/artifacts/__tests__/fixtures/contract1.json +0 -19
  65. package/src/lib/artifacts/__tests__/fixtures/contract2.json +0 -19
  66. package/src/lib/artifacts/__tests__/fixtures/duplicate-name.json +0 -19
  67. package/src/lib/artifacts/__tests__/fixtures/nested/nested-contract.json +0 -18
  68. package/src/lib/artifacts/__tests__/fixtures/not-an-artifact.json +0 -8
  69. package/src/lib/artifacts/__tests__/fixtures/readme.txt +0 -2
  70. package/src/lib/contracts/__tests__/repository.spec.ts +0 -612
  71. package/src/lib/contracts/repository.ts +0 -411
  72. package/src/lib/core/__tests__/assert-action.spec.ts +0 -474
  73. package/src/lib/core/__tests__/context.spec.ts +0 -37
  74. package/src/lib/core/__tests__/engine.spec.ts +0 -2005
  75. package/src/lib/core/__tests__/graph.spec.ts +0 -125
  76. package/src/lib/core/__tests__/json-integration.spec.ts +0 -425
  77. package/src/lib/core/__tests__/loader.spec.ts +0 -367
  78. package/src/lib/core/__tests__/multi-platform-verification.spec.ts +0 -406
  79. package/src/lib/core/__tests__/resolver.spec.ts +0 -2496
  80. package/src/lib/core/__tests__/static-action.spec.ts +0 -172
  81. package/src/lib/core/context.ts +0 -127
  82. package/src/lib/core/engine.ts +0 -1834
  83. package/src/lib/core/graph.ts +0 -252
  84. package/src/lib/core/loader.ts +0 -253
  85. package/src/lib/core/resolver.ts +0 -873
  86. package/src/lib/deployer.ts +0 -1005
  87. package/src/lib/events/__tests__/event-system.spec.ts +0 -392
  88. package/src/lib/events/cli-adapter.ts +0 -369
  89. package/src/lib/events/emitter.ts +0 -62
  90. package/src/lib/events/index.ts +0 -3
  91. package/src/lib/events/types.ts +0 -520
  92. package/src/lib/index.ts +0 -17
  93. package/src/lib/network-loader.ts +0 -90
  94. package/src/lib/network-selection.ts +0 -73
  95. package/src/lib/network-utils.ts +0 -64
  96. package/src/lib/parsers/__tests__/buildinfo.spec.ts +0 -122
  97. package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-bytecode-buildinfo.json +0 -62
  98. package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-json.txt +0 -2
  99. package/src/lib/parsers/__tests__/fixtures/buildinfo/multi-contract-buildinfo.json +0 -89
  100. package/src/lib/parsers/__tests__/fixtures/buildinfo/no-contracts-buildinfo.json +0 -17
  101. package/src/lib/parsers/__tests__/fixtures/buildinfo/simple-buildinfo.json +0 -63
  102. package/src/lib/parsers/__tests__/fixtures/buildinfo/wrong-format.json +0 -4
  103. package/src/lib/parsers/__tests__/job.spec.ts +0 -439
  104. package/src/lib/parsers/__tests__/source.spec.ts +0 -134
  105. package/src/lib/parsers/__tests__/template.spec.ts +0 -111
  106. package/src/lib/parsers/artifact/__tests__/artifact.spec.ts +0 -117
  107. package/src/lib/parsers/artifact/__tests__/fixtures/empty-bytecode.json +0 -5
  108. package/src/lib/parsers/artifact/__tests__/fixtures/hardhat-artifact.json +0 -67
  109. package/src/lib/parsers/artifact/__tests__/fixtures/invalid-bytecode.json +0 -5
  110. package/src/lib/parsers/artifact/__tests__/fixtures/invalid-json.txt +0 -11
  111. package/src/lib/parsers/artifact/__tests__/fixtures/minimal-artifact.json +0 -5
  112. package/src/lib/parsers/artifact/__tests__/fixtures/missing-abi.json +0 -4
  113. package/src/lib/parsers/artifact/__tests__/fixtures/missing-bytecode.json +0 -11
  114. package/src/lib/parsers/artifact/__tests__/fixtures/missing-contract-name.json +0 -11
  115. package/src/lib/parsers/artifact/__tests__/fixtures/simple-artifact.json +0 -40
  116. package/src/lib/parsers/artifact/__tests__/fixtures/wrong-types.json +0 -7
  117. package/src/lib/parsers/artifact/foundry-1.2.ts +0 -72
  118. package/src/lib/parsers/artifact/index.ts +0 -27
  119. package/src/lib/parsers/artifact/types.ts +0 -9
  120. package/src/lib/parsers/buildinfo.ts +0 -127
  121. package/src/lib/parsers/constants.ts +0 -56
  122. package/src/lib/parsers/index.ts +0 -6
  123. package/src/lib/parsers/job.ts +0 -160
  124. package/src/lib/parsers/source.ts +0 -129
  125. package/src/lib/parsers/template.ts +0 -135
  126. package/src/lib/provenance.ts +0 -785
  127. package/src/lib/std/templates/arachnid-deterministic-deployment-proxy.yaml +0 -68
  128. package/src/lib/std/templates/assured-deployment.yaml +0 -46
  129. package/src/lib/std/templates/era-evm-predeploy.yaml +0 -35
  130. package/src/lib/std/templates/erc-2470.yaml +0 -70
  131. package/src/lib/std/templates/min-balance.yaml +0 -35
  132. package/src/lib/std/templates/nano-universal-deployer.yaml +0 -61
  133. package/src/lib/std/templates/raw-erc-2470.yaml +0 -62
  134. package/src/lib/std/templates/raw-nano-universal-deployer.yaml +0 -54
  135. package/src/lib/std/templates/raw-sequence-universal-deployer-2.yaml +0 -52
  136. package/src/lib/std/templates/sequence-universal-deployer-2.yaml +0 -61
  137. package/src/lib/types/__tests__/json-request-action.spec.ts +0 -243
  138. package/src/lib/types/__tests__/read-json-value.spec.ts +0 -278
  139. package/src/lib/types/__tests__/resolve-json-value.spec.ts +0 -769
  140. package/src/lib/types/actions.ts +0 -148
  141. package/src/lib/types/artifacts.ts +0 -21
  142. package/src/lib/types/buildinfo.ts +0 -116
  143. package/src/lib/types/conditions.ts +0 -50
  144. package/src/lib/types/contracts.ts +0 -26
  145. package/src/lib/types/definitions.ts +0 -77
  146. package/src/lib/types/index.ts +0 -9
  147. package/src/lib/types/network.ts +0 -33
  148. package/src/lib/types/project.ts +0 -9
  149. package/src/lib/types/source.ts +0 -26
  150. package/src/lib/types/task.ts +0 -9
  151. package/src/lib/types/values.ts +0 -221
  152. package/src/lib/utils/assertion.ts +0 -24
  153. package/src/lib/utils/validation.ts +0 -116
  154. package/src/lib/validation/contract-references.ts +0 -210
  155. package/src/lib/validation/index.ts +0 -1
  156. package/src/lib/verification/__tests__/etherscan.spec.ts +0 -710
  157. package/src/lib/verification/__tests__/sourcify.spec.ts +0 -288
  158. package/src/lib/verification/etherscan.ts +0 -547
  159. package/src/lib/verification/sourcify.ts +0 -248
  160. package/test_validation/artifacts/TestContract.json +0 -9
  161. package/test_validation/jobs/test-missing.yaml +0 -16
  162. package/test_validation/networks.yaml +0 -3
  163. package/tsconfig.json +0 -36
@@ -1,215 +0,0 @@
1
- import { Command } from 'commander'
2
- import chalk from 'chalk'
3
- import * as fs from 'fs'
4
- import * as path from 'path'
5
- import { projectOption, verbosityOption } from './common'
6
- import { loadNetworks } from '../lib/network-loader'
7
- import { setVerbosity } from '../index'
8
-
9
- interface UtilsOptions {
10
- project: string
11
- verbose: number
12
- }
13
-
14
- export function makeUtilsCommand(): Command {
15
- const utils = new Command('utils')
16
- .description('Utility commands for project management')
17
-
18
- const chainIdToName = new Command('chain-id-to-name')
19
- .description('Convert a chain ID to network name')
20
- projectOption(chainIdToName)
21
- verbosityOption(chainIdToName)
22
-
23
- chainIdToName.argument('<chain-id>', 'The chain ID to convert')
24
- chainIdToName.action(async (chainId: string, options: UtilsOptions) => {
25
- try {
26
- // Set verbosity level for logging
27
- setVerbosity(options.verbose as 0 | 1 | 2 | 3)
28
-
29
- const chainIdNumber = parseInt(chainId, 10)
30
- if (isNaN(chainIdNumber)) {
31
- console.error(chalk.red('Invalid chain ID. Please provide a valid number.'))
32
- process.exit(1)
33
- }
34
-
35
- const networks = await loadNetworks(options.project)
36
-
37
- const network = networks.find(n => n.chainId === chainIdNumber)
38
-
39
- if (network) {
40
- console.log(network.name)
41
- } else {
42
- console.error(chalk.red(`No network found with chain ID ${chainIdNumber}`))
43
- process.exit(1)
44
- }
45
- } catch (error) {
46
- console.error(chalk.red('Error converting chain ID to network name:'), error instanceof Error ? error.message : String(error))
47
- process.exit(1)
48
- }
49
- })
50
-
51
- utils.addCommand(chainIdToName)
52
-
53
- // utils gen-table <output-dir>
54
- const genTable = new Command('gen-table')
55
- .description('Generate a consolidated addresses table from an output directory')
56
- .argument('<output-dir>', 'Directory containing job output JSON files (searches recursively)')
57
- .option('--name', 'Include Name column', true)
58
- .option('--key', 'Include Key column', false)
59
- .option('--file', 'Include File column', false)
60
- .option('--chain-ids, --chainIds', 'Include ChainIds column', false)
61
- .option('--job', 'Include Job column', true)
62
- .option('--address', 'Include Address column', true)
63
- .option('--format <format>', "Output format: 'markdown' or 'ascii' (default)", 'ascii')
64
- .action(async (outputDir: string, options: { name?: boolean; key?: boolean; file?: boolean; chainIds?: boolean; job?: boolean; address?: boolean; format?: string }) => {
65
- try {
66
- const absoluteDir = path.resolve(outputDir)
67
- if (!fs.existsSync(absoluteDir) || !fs.statSync(absoluteDir).isDirectory()) {
68
- console.error(chalk.red(`Output directory not found or not a directory: ${absoluteDir}`))
69
- process.exit(1)
70
- }
71
-
72
- const jsonFiles: string[] = []
73
- const walk = (dir: string) => {
74
- for (const entry of fs.readdirSync(dir)) {
75
- const full = path.join(dir, entry)
76
- const stat = fs.statSync(full)
77
- if (stat.isDirectory()) walk(full)
78
- else if (stat.isFile() && entry.toLowerCase().endsWith('.json')) jsonFiles.push(full)
79
- }
80
- }
81
- walk(absoluteDir)
82
-
83
- type Row = { job: string; chainIds: string; name: string; address: string; key: string; file: string }
84
- const rows: Row[] = []
85
- const addressRegex = /^0x[a-fA-F0-9]{40}$/
86
-
87
- const toTitleCase = (slug: string): string => slug.split(/[-_\s]+/).filter(Boolean).map(s => s.charAt(0).toUpperCase() + s.slice(1)).join('')
88
- const extractVersionSuffix = (jobName: string): string => {
89
- const m = jobName.match(/[-_]?v(\d+)/i)
90
- return m ? `V${m[1]}` : ''
91
- }
92
- const deriveName = (jobName: string, key: string): string => {
93
- const version = extractVersionSuffix(jobName)
94
- const baseJob = jobName.replace(/[-_]?v\d+$/i, '')
95
- const keyCore = key.replace(/\.address$/i, '')
96
- // Prefer descriptive key name; if too generic like 'factory', prefix with job base
97
- const isGeneric = /^(factory|address)$/i.test(keyCore)
98
- const nameCore = isGeneric ? `${toTitleCase(baseJob)} ${toTitleCase(keyCore)}` : toTitleCase(keyCore)
99
- return `${nameCore.replace(/\s+/g, '')}${version}`
100
- }
101
-
102
- for (const file of jsonFiles) {
103
- try {
104
- const raw = fs.readFileSync(file, 'utf8')
105
- const data = JSON.parse(raw)
106
- if (!data || typeof data !== 'object' || !Array.isArray(data.networks)) continue
107
- const jobName: string = data.jobName ?? path.basename(file, '.json')
108
- for (const net of data.networks) {
109
- if (!net || typeof net !== 'object') continue
110
- const outputs = net.outputs as Record<string, unknown> | undefined
111
- if (!outputs) continue
112
- const chainIds: string[] = Array.isArray(net.chainIds) ? net.chainIds : []
113
- for (const [key, value] of Object.entries(outputs)) {
114
- let address: string | undefined
115
- if (typeof value === 'string' && addressRegex.test(value)) {
116
- address = value
117
- } else if (value && typeof value === 'object' && 'address' in value && typeof value.address === 'string' && addressRegex.test(value.address)) {
118
- address = value.address
119
- }
120
- if (!address) continue
121
- rows.push({
122
- job: jobName,
123
- chainIds: chainIds.join(','),
124
- name: deriveName(jobName, key),
125
- address,
126
- key,
127
- file
128
- })
129
- }
130
- }
131
- } catch {
132
- // skip invalid JSON
133
- }
134
- }
135
-
136
- rows.sort((a, b) => a.job.localeCompare(b.job) || a.name.localeCompare(b.name))
137
-
138
- if (rows.length === 0) {
139
- console.log(chalk.yellow('No address entries found.'))
140
- return
141
- }
142
-
143
- // Determine which columns to show
144
- const showJob = !!options.job
145
- const showAddress = !!options.address
146
- const showName = !!options.name
147
- const showKey = !!options.key
148
- const showChainIds = !!options.chainIds
149
- const showFile = !!options.file
150
-
151
- const selectedHeaders: (keyof Row)[] = []
152
- if (showJob) selectedHeaders.push('job')
153
- if (showChainIds) selectedHeaders.push('chainIds')
154
- if (showName) selectedHeaders.push('name')
155
- if (showAddress) selectedHeaders.push('address')
156
- if (showKey) selectedHeaders.push('key')
157
- if (showFile) selectedHeaders.push('file')
158
-
159
- // Titles for columns
160
- const titles: Record<keyof Row, string> = {
161
- job: 'Job',
162
- chainIds: 'ChainIds',
163
- name: 'Name',
164
- address: 'Address',
165
- key: 'Key',
166
- file: 'File'
167
- }
168
- const format = String(options.format || 'markdown').toLowerCase()
169
- if (format !== 'markdown' && format !== 'ascii') {
170
- console.error(chalk.red("Invalid format. Use 'markdown' or 'ascii'."))
171
- process.exit(1)
172
- }
173
-
174
- if (format === 'markdown') {
175
- const header = '| ' + selectedHeaders.map(h => titles[h]).join(' | ') + ' |'
176
- const sepMd = '| ' + selectedHeaders.map(h => '-'.repeat(Math.max(3, String(titles[h]).length))).join(' | ') + ' |'
177
- console.log(header)
178
- console.log(sepMd)
179
- for (const r of rows) {
180
- console.log('| ' + selectedHeaders.map(h => String(r[h])).join(' | ') + ' |')
181
- }
182
- } else {
183
- // ascii rendering with box-drawing characters
184
- const widths: Record<string, number> = {}
185
- for (const h of selectedHeaders) {
186
- widths[h] = Math.max(titles[h].length, ...rows.map(r => String(r[h]).length))
187
- }
188
- const makeSep = (left: string, mid: string, right: string, fill: string) => {
189
- return left + selectedHeaders.map(h => fill.repeat(widths[h] + 2)).join(mid) + right
190
- }
191
- const pad = (s: string, w: number) => s + ' '.repeat(Math.max(0, w - s.length))
192
-
193
- const top = makeSep('┌', '┬', '┐', '─')
194
- const sep = makeSep('├', '┼', '┤', '─')
195
- const bot = makeSep('└', '┴', '┘', '─')
196
- const headerLine = '│' + selectedHeaders.map(h => ' ' + pad(titles[h], widths[h]) + ' ').join('│') + '│'
197
- const lines = rows.map(r => '│' + selectedHeaders.map(h => ' ' + pad(String(r[h]), widths[h]) + ' ').join('│') + '│')
198
-
199
- console.log(top)
200
- console.log(headerLine)
201
- console.log(sep)
202
- for (const line of lines) console.log(line)
203
- console.log(bot)
204
- }
205
-
206
- } catch (error) {
207
- console.error(chalk.red('Error generating table:'), error instanceof Error ? error.message : String(error))
208
- process.exit(1)
209
- }
210
- })
211
-
212
- utils.addCommand(genTable)
213
-
214
- return utils
215
- }
package/src/index.ts DELETED
@@ -1,67 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { program } from 'commander'
4
- import chalk from 'chalk'
5
- import { setupCommands } from './cli'
6
- import packageJson from '../package.json'
7
-
8
- import { deploymentEvents, CLIEventAdapter, VerbosityLevel } from './lib/events'
9
-
10
- // Set up CLI event adapter to convert events to console output
11
- const cliAdapter = new CLIEventAdapter(deploymentEvents)
12
-
13
- // Export function to update CLI verbosity
14
- export function setVerbosity(level: VerbosityLevel): void {
15
- cliAdapter.setVerbosity(level)
16
- }
17
-
18
- // Setup global error handling
19
- process.on('unhandledRejection', (reason, promise) => {
20
- deploymentEvents.emitEvent({
21
- type: 'unhandled_rejection',
22
- level: 'error',
23
- data: {
24
- reason,
25
- promise
26
- }
27
- })
28
- process.exit(1)
29
- })
30
-
31
- process.on('uncaughtException', (error) => {
32
- deploymentEvents.emitEvent({
33
- type: 'uncaught_exception',
34
- level: 'error',
35
- data: {
36
- error
37
- }
38
- })
39
- process.exit(1)
40
- })
41
-
42
- async function main() {
43
- try {
44
- // Configure the main program
45
- program
46
- .name('catapult')
47
- .description('Ethereum contract deployment CLI tool')
48
- .version(packageJson.version)
49
-
50
- // Setup all commands
51
- setupCommands(program)
52
-
53
- // Parse arguments
54
- await program.parseAsync(process.argv)
55
- } catch (error) {
56
- deploymentEvents.emitEvent({
57
- type: 'cli_error',
58
- level: 'error',
59
- data: {
60
- message: error instanceof Error ? error.message : String(error)
61
- }
62
- })
63
- process.exit(1)
64
- }
65
- }
66
-
67
- main()
@@ -1,338 +0,0 @@
1
- import { Deployer, DeployerOptions } from '../deployer'
2
- import { DeploymentEventEmitter } from '../events'
3
- import { Network } from '../types'
4
-
5
- // Mock the ProjectLoader to avoid file system dependencies in tests
6
- jest.mock('../core/loader', () => {
7
- return {
8
- ProjectLoader: jest.fn().mockImplementation(() => {
9
- const mockJobs = new Map([
10
- ['test-job', {
11
- name: 'test-job',
12
- version: '1.0.0',
13
- actions: [],
14
- depends_on: []
15
- }]
16
- ])
17
-
18
- const mockTemplates = new Map()
19
-
20
- return {
21
- load: jest.fn().mockResolvedValue(undefined),
22
- jobs: mockJobs,
23
- templates: mockTemplates,
24
- contractRepository: {
25
- lookup: jest.fn(),
26
- getAll: jest.fn()
27
- }
28
- }
29
- })
30
- }
31
- })
32
-
33
- // Mock the DependencyGraph
34
- jest.mock('../core/graph', () => {
35
- return {
36
- DependencyGraph: jest.fn().mockImplementation(() => ({
37
- getExecutionOrder: jest.fn().mockReturnValue(['test-job']),
38
- getDependencies: jest.fn().mockReturnValue(new Set())
39
- }))
40
- }
41
- })
42
-
43
- // Mock the ExecutionEngine
44
- jest.mock('../core/engine', () => {
45
- return {
46
- ExecutionEngine: jest.fn().mockImplementation(() => ({
47
- executeJob: jest.fn().mockResolvedValue(undefined)
48
- }))
49
- }
50
- })
51
-
52
- // Mock the ExecutionContext to avoid ethers validation
53
- jest.mock('../core/context', () => {
54
- return {
55
- ExecutionContext: jest.fn().mockImplementation(() => ({
56
- getNetwork: jest.fn().mockReturnValue({
57
- name: 'localhost',
58
- chainId: 1337,
59
- rpcUrl: 'http://localhost:8545'
60
- }),
61
- setOutput: jest.fn(),
62
- getOutput: jest.fn(),
63
- getOutputs: jest.fn().mockReturnValue(new Map())
64
- }))
65
- }
66
- })
67
-
68
- // Mock fs operations
69
- jest.mock('fs/promises', () => ({
70
- mkdir: jest.fn().mockResolvedValue(undefined),
71
- writeFile: jest.fn().mockResolvedValue(undefined)
72
- }))
73
-
74
- describe('Deployer Event Integration', () => {
75
- let deployer: Deployer
76
- let eventEmitter: DeploymentEventEmitter
77
- let emittedEvents: any[]
78
-
79
- const mockOptions: DeployerOptions = {
80
- projectRoot: '/test/project',
81
- privateKey: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12',
82
- networks: [
83
- {
84
- name: 'localhost',
85
- chainId: 1337,
86
- rpcUrl: 'http://localhost:8545'
87
- } as Network
88
- ]
89
- }
90
-
91
- beforeEach(() => {
92
- emittedEvents = []
93
- eventEmitter = new DeploymentEventEmitter()
94
-
95
- // Capture all emitted events
96
- eventEmitter.onAnyEvent((event) => {
97
- emittedEvents.push(event)
98
- })
99
-
100
- deployer = new Deployer({
101
- ...mockOptions,
102
- eventEmitter
103
- })
104
- })
105
-
106
- describe('Deployment Lifecycle Events', () => {
107
- it('should emit deployment_started event when run begins', async () => {
108
- await expect(deployer.run()).resolves.not.toThrow()
109
-
110
- const startEvent = emittedEvents.find(e => e.type === 'deployment_started')
111
- expect(startEvent).toBeDefined()
112
- expect(startEvent.data.projectRoot).toBe('/test/project')
113
- expect(startEvent.level).toBe('info')
114
- expect(startEvent.timestamp).toBeInstanceOf(Date)
115
- })
116
-
117
- it('should emit project loading events', async () => {
118
- await expect(deployer.run()).resolves.not.toThrow()
119
-
120
- const loadingStartedEvent = emittedEvents.find(e => e.type === 'project_loading_started')
121
- expect(loadingStartedEvent).toBeDefined()
122
- expect(loadingStartedEvent.data.projectRoot).toBe('/test/project')
123
-
124
- const loadedEvent = emittedEvents.find(e => e.type === 'project_loaded')
125
- expect(loadedEvent).toBeDefined()
126
- expect(loadedEvent.data.jobCount).toBeDefined()
127
- expect(loadedEvent.data.templateCount).toBeDefined()
128
- })
129
-
130
- it('should emit execution plan event', async () => {
131
- await expect(deployer.run()).resolves.not.toThrow()
132
-
133
- const planEvent = emittedEvents.find(e => e.type === 'execution_plan')
134
- expect(planEvent).toBeDefined()
135
- expect(planEvent.data.targetNetworks).toEqual([{
136
- name: 'localhost',
137
- chainId: 1337
138
- }])
139
- expect(planEvent.data.jobExecutionOrder).toEqual(['test-job'])
140
- })
141
-
142
- it('should emit network_started event for each network', async () => {
143
- await expect(deployer.run()).resolves.not.toThrow()
144
-
145
- const networkEvent = emittedEvents.find(e => e.type === 'network_started')
146
- expect(networkEvent).toBeDefined()
147
- expect(networkEvent.data.networkName).toBe('localhost')
148
- expect(networkEvent.data.chainId).toBe(1337)
149
- })
150
-
151
- it('should emit deployment_completed event on success', async () => {
152
- await expect(deployer.run()).resolves.not.toThrow()
153
-
154
- const completedEvent = emittedEvents.find(e => e.type === 'deployment_completed')
155
- expect(completedEvent).toBeDefined()
156
- expect(completedEvent.level).toBe('info')
157
- })
158
-
159
- it('should emit output writing events', async () => {
160
- // Mock context.getOutputs to return some outputs
161
- const { ExecutionEngine } = require('../core/engine')
162
- ExecutionEngine.mockImplementation(() => ({
163
- executeJob: jest.fn().mockImplementation((job, context) => {
164
- // Mock the getOutputs method
165
- context.getOutputs = jest.fn().mockReturnValue(new Map([
166
- ['contract.address', '0x1234567890123456789012345678901234567890']
167
- ]))
168
- })
169
- }))
170
-
171
- deployer = new Deployer({
172
- ...mockOptions,
173
- eventEmitter
174
- })
175
-
176
- await expect(deployer.run()).resolves.not.toThrow()
177
-
178
- const outputWritingEvent = emittedEvents.find(e => e.type === 'output_writing_started')
179
- expect(outputWritingEvent).toBeDefined()
180
-
181
- const outputFileEvent = emittedEvents.find(e => e.type === 'output_file_written')
182
- expect(outputFileEvent).toBeDefined()
183
- expect(outputFileEvent.data.relativePath).toBe('output/test-job.json')
184
- })
185
- })
186
-
187
- describe('Error Handling', () => {
188
- it('should emit deployment_failed event on error', async () => {
189
- // Mock the loader to throw an error
190
- const { ProjectLoader } = require('../core/loader')
191
- ProjectLoader.mockImplementation(() => ({
192
- load: jest.fn().mockRejectedValue(new Error('Test error'))
193
- }))
194
-
195
- deployer = new Deployer({
196
- ...mockOptions,
197
- eventEmitter
198
- })
199
-
200
- await expect(deployer.run()).rejects.toThrow('Test error')
201
-
202
- const failedEvent = emittedEvents.find(e => e.type === 'deployment_failed')
203
- expect(failedEvent).toBeDefined()
204
- expect(failedEvent.level).toBe('error')
205
- expect(failedEvent.data.error).toBe('Test error')
206
- expect(failedEvent.data.stack).toBeDefined()
207
- })
208
- })
209
-
210
- describe('Network Filtering', () => {
211
- beforeEach(() => {
212
- // Reset mocks for this test group
213
- jest.clearAllMocks()
214
-
215
- // Reset the ProjectLoader mock to success
216
- const { ProjectLoader } = require('../core/loader')
217
- ProjectLoader.mockImplementation(() => ({
218
- load: jest.fn().mockResolvedValue(undefined),
219
- jobs: new Map([
220
- ['test-job', {
221
- name: 'test-job',
222
- version: '1.0.0',
223
- actions: [],
224
- depends_on: []
225
- }]
226
- ]),
227
- templates: new Map(),
228
- contractRepository: {
229
- lookup: jest.fn(),
230
- getAll: jest.fn()
231
- }
232
- }))
233
-
234
- emittedEvents.length = 0 // Clear events array
235
- })
236
-
237
- it('should emit missing_network_config_warning for unknown chain IDs', async () => {
238
- deployer = new Deployer({
239
- ...mockOptions,
240
- runOnNetworks: [1337, 999], // 999 doesn't exist
241
- eventEmitter
242
- })
243
-
244
- await expect(deployer.run()).resolves.not.toThrow()
245
-
246
- const warningEvent = emittedEvents.find(e => e.type === 'missing_network_config_warning')
247
- expect(warningEvent).toBeDefined()
248
- expect(warningEvent.data.missingChainIds).toEqual([999])
249
- })
250
-
251
- // Note: Testing no_outputs event is complex in integration tests
252
- // as it requires specific job execution scenarios. The event itself
253
- // is tested in the main deployer tests.
254
- })
255
-
256
- describe('Event Ordering', () => {
257
- beforeEach(() => {
258
- // Reset mocks and create fresh deployer for this test group
259
- jest.clearAllMocks()
260
-
261
- // Reset the ProjectLoader mock to success
262
- const { ProjectLoader } = require('../core/loader')
263
- ProjectLoader.mockImplementation(() => ({
264
- load: jest.fn().mockResolvedValue(undefined),
265
- jobs: new Map([
266
- ['test-job', {
267
- name: 'test-job',
268
- version: '1.0.0',
269
- actions: [],
270
- depends_on: []
271
- }]
272
- ]),
273
- templates: new Map(),
274
- contractRepository: {
275
- lookup: jest.fn(),
276
- getAll: jest.fn()
277
- }
278
- }))
279
-
280
- emittedEvents.length = 0 // Clear events array
281
-
282
- deployer = new Deployer({
283
- ...mockOptions,
284
- eventEmitter
285
- })
286
- })
287
-
288
- it('should emit events in the correct order', async () => {
289
- await expect(deployer.run()).resolves.not.toThrow()
290
-
291
- const eventTypes = emittedEvents.map(e => e.type)
292
-
293
- // Check that events are in a logical order
294
- expect(eventTypes.indexOf('deployment_started')).toBeLessThan(
295
- eventTypes.indexOf('project_loading_started')
296
- )
297
- expect(eventTypes.indexOf('project_loaded')).toBeLessThan(
298
- eventTypes.indexOf('execution_plan')
299
- )
300
- expect(eventTypes.indexOf('execution_plan')).toBeLessThan(
301
- eventTypes.indexOf('network_started')
302
- )
303
- expect(eventTypes.indexOf('network_started')).toBeLessThan(
304
- eventTypes.indexOf('deployment_completed')
305
- )
306
- })
307
-
308
- it('should have timestamps in chronological order', async () => {
309
- deployer = new Deployer({
310
- ...mockOptions,
311
- eventEmitter
312
- })
313
-
314
- await expect(deployer.run()).resolves.not.toThrow()
315
-
316
- // Check that timestamps are increasing
317
- for (let i = 1; i < emittedEvents.length; i++) {
318
- expect(emittedEvents[i].timestamp.getTime()).toBeGreaterThanOrEqual(
319
- emittedEvents[i - 1].timestamp.getTime()
320
- )
321
- }
322
- })
323
- })
324
-
325
- describe('Event Emitter Access', () => {
326
- it('should expose the event emitter as a public property', () => {
327
- expect(deployer.events).toBe(eventEmitter)
328
- expect(deployer.events).toBeInstanceOf(DeploymentEventEmitter)
329
- })
330
-
331
- it('should use global singleton when no custom emitter provided', () => {
332
- const { deploymentEvents } = require('../events')
333
- const deployerWithoutCustomEmitter = new Deployer(mockOptions)
334
-
335
- expect(deployerWithoutCustomEmitter.events).toBe(deploymentEvents)
336
- })
337
- })
338
- })