@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.
- package/README.md +27 -0
- package/dist/lib/__tests__/network-loader.spec.js.map +1 -1
- package/dist/lib/core/__tests__/resolver.spec.js +22 -0
- package/dist/lib/core/__tests__/resolver.spec.js.map +1 -1
- package/dist/lib/core/__tests__/sign-actions.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/sign-actions.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/sign-actions.spec.js +128 -0
- package/dist/lib/core/__tests__/sign-actions.spec.js.map +1 -0
- package/dist/lib/core/__tests__/signer.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/signer.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/signer.spec.js +40 -0
- package/dist/lib/core/__tests__/signer.spec.js.map +1 -0
- package/dist/lib/core/context.d.ts +3 -2
- package/dist/lib/core/context.d.ts.map +1 -1
- package/dist/lib/core/context.js +3 -2
- package/dist/lib/core/context.js.map +1 -1
- package/dist/lib/core/engine.d.ts +4 -0
- package/dist/lib/core/engine.d.ts.map +1 -1
- package/dist/lib/core/engine.js +173 -0
- package/dist/lib/core/engine.js.map +1 -1
- package/dist/lib/core/signer.d.ts +7 -0
- package/dist/lib/core/signer.d.ts.map +1 -0
- package/dist/lib/core/signer.js +60 -0
- package/dist/lib/core/signer.js.map +1 -0
- package/dist/lib/parsers/__tests__/source.spec.js +37 -0
- package/dist/lib/parsers/__tests__/source.spec.js.map +1 -1
- package/dist/lib/parsers/source.js +1 -1
- package/dist/lib/parsers/source.js.map +1 -1
- package/dist/lib/provenance.js +51 -2
- package/dist/lib/provenance.js.map +1 -1
- package/dist/lib/types/actions.d.ts +26 -2
- package/dist/lib/types/actions.d.ts.map +1 -1
- package/dist/lib/types/actions.js +3 -0
- package/dist/lib/types/actions.js.map +1 -1
- package/dist/lib/types/source.d.ts +2 -0
- package/dist/lib/types/source.d.ts.map +1 -1
- package/package.json +4 -1
- package/.eslintrc.json +0 -29
- package/.github/workflows/ci.yml +0 -181
- package/CONCEPT.md +0 -24
- package/contracts/checked-call.huff +0 -65
- package/eslint.config.js +0 -48
- package/examples/jobs/guards-v1.yaml +0 -17
- package/examples/jobs/sequence-seq-0001-patch.yaml +0 -59
- package/examples/jobs/sequence-v1.yaml +0 -59
- package/examples/templates/sequence-factory-v1.yaml +0 -56
- package/jest.config.js +0 -25
- package/src/cli.ts +0 -18
- package/src/commands/common.ts +0 -61
- package/src/commands/dry.ts +0 -209
- package/src/commands/etherscan.ts +0 -360
- package/src/commands/index.ts +0 -6
- package/src/commands/list.ts +0 -262
- package/src/commands/provenance.ts +0 -120
- package/src/commands/run.ts +0 -146
- package/src/commands/utils.ts +0 -215
- package/src/index.ts +0 -67
- package/src/lib/__tests__/deployer-events.spec.ts +0 -338
- package/src/lib/__tests__/deployer.spec.ts +0 -2269
- package/src/lib/__tests__/network-loader.spec.ts +0 -150
- package/src/lib/__tests__/network-selection.spec.ts +0 -41
- package/src/lib/__tests__/network-utils.spec.ts +0 -230
- package/src/lib/__tests__/provenance.spec.ts +0 -208
- package/src/lib/artifacts/__tests__/fixtures/contract1.json +0 -19
- package/src/lib/artifacts/__tests__/fixtures/contract2.json +0 -19
- package/src/lib/artifacts/__tests__/fixtures/duplicate-name.json +0 -19
- package/src/lib/artifacts/__tests__/fixtures/nested/nested-contract.json +0 -18
- package/src/lib/artifacts/__tests__/fixtures/not-an-artifact.json +0 -8
- package/src/lib/artifacts/__tests__/fixtures/readme.txt +0 -2
- package/src/lib/contracts/__tests__/repository.spec.ts +0 -612
- package/src/lib/contracts/repository.ts +0 -411
- package/src/lib/core/__tests__/assert-action.spec.ts +0 -474
- package/src/lib/core/__tests__/context.spec.ts +0 -37
- package/src/lib/core/__tests__/engine.spec.ts +0 -2005
- package/src/lib/core/__tests__/graph.spec.ts +0 -125
- package/src/lib/core/__tests__/json-integration.spec.ts +0 -425
- package/src/lib/core/__tests__/loader.spec.ts +0 -367
- package/src/lib/core/__tests__/multi-platform-verification.spec.ts +0 -406
- package/src/lib/core/__tests__/resolver.spec.ts +0 -2496
- package/src/lib/core/__tests__/static-action.spec.ts +0 -172
- package/src/lib/core/context.ts +0 -127
- package/src/lib/core/engine.ts +0 -1834
- package/src/lib/core/graph.ts +0 -252
- package/src/lib/core/loader.ts +0 -253
- package/src/lib/core/resolver.ts +0 -873
- package/src/lib/deployer.ts +0 -1005
- package/src/lib/events/__tests__/event-system.spec.ts +0 -392
- package/src/lib/events/cli-adapter.ts +0 -369
- package/src/lib/events/emitter.ts +0 -62
- package/src/lib/events/index.ts +0 -3
- package/src/lib/events/types.ts +0 -520
- package/src/lib/index.ts +0 -17
- package/src/lib/network-loader.ts +0 -90
- package/src/lib/network-selection.ts +0 -73
- package/src/lib/network-utils.ts +0 -64
- package/src/lib/parsers/__tests__/buildinfo.spec.ts +0 -122
- package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-bytecode-buildinfo.json +0 -62
- package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-json.txt +0 -2
- package/src/lib/parsers/__tests__/fixtures/buildinfo/multi-contract-buildinfo.json +0 -89
- package/src/lib/parsers/__tests__/fixtures/buildinfo/no-contracts-buildinfo.json +0 -17
- package/src/lib/parsers/__tests__/fixtures/buildinfo/simple-buildinfo.json +0 -63
- package/src/lib/parsers/__tests__/fixtures/buildinfo/wrong-format.json +0 -4
- package/src/lib/parsers/__tests__/job.spec.ts +0 -439
- package/src/lib/parsers/__tests__/source.spec.ts +0 -134
- package/src/lib/parsers/__tests__/template.spec.ts +0 -111
- package/src/lib/parsers/artifact/__tests__/artifact.spec.ts +0 -117
- package/src/lib/parsers/artifact/__tests__/fixtures/empty-bytecode.json +0 -5
- package/src/lib/parsers/artifact/__tests__/fixtures/hardhat-artifact.json +0 -67
- package/src/lib/parsers/artifact/__tests__/fixtures/invalid-bytecode.json +0 -5
- package/src/lib/parsers/artifact/__tests__/fixtures/invalid-json.txt +0 -11
- package/src/lib/parsers/artifact/__tests__/fixtures/minimal-artifact.json +0 -5
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-abi.json +0 -4
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-bytecode.json +0 -11
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-contract-name.json +0 -11
- package/src/lib/parsers/artifact/__tests__/fixtures/simple-artifact.json +0 -40
- package/src/lib/parsers/artifact/__tests__/fixtures/wrong-types.json +0 -7
- package/src/lib/parsers/artifact/foundry-1.2.ts +0 -72
- package/src/lib/parsers/artifact/index.ts +0 -27
- package/src/lib/parsers/artifact/types.ts +0 -9
- package/src/lib/parsers/buildinfo.ts +0 -127
- package/src/lib/parsers/constants.ts +0 -56
- package/src/lib/parsers/index.ts +0 -6
- package/src/lib/parsers/job.ts +0 -160
- package/src/lib/parsers/source.ts +0 -129
- package/src/lib/parsers/template.ts +0 -135
- package/src/lib/provenance.ts +0 -785
- package/src/lib/std/templates/arachnid-deterministic-deployment-proxy.yaml +0 -68
- package/src/lib/std/templates/assured-deployment.yaml +0 -46
- package/src/lib/std/templates/era-evm-predeploy.yaml +0 -35
- package/src/lib/std/templates/erc-2470.yaml +0 -70
- package/src/lib/std/templates/min-balance.yaml +0 -35
- package/src/lib/std/templates/nano-universal-deployer.yaml +0 -61
- package/src/lib/std/templates/raw-erc-2470.yaml +0 -62
- package/src/lib/std/templates/raw-nano-universal-deployer.yaml +0 -54
- package/src/lib/std/templates/raw-sequence-universal-deployer-2.yaml +0 -52
- package/src/lib/std/templates/sequence-universal-deployer-2.yaml +0 -61
- package/src/lib/types/__tests__/json-request-action.spec.ts +0 -243
- package/src/lib/types/__tests__/read-json-value.spec.ts +0 -278
- package/src/lib/types/__tests__/resolve-json-value.spec.ts +0 -769
- package/src/lib/types/actions.ts +0 -148
- package/src/lib/types/artifacts.ts +0 -21
- package/src/lib/types/buildinfo.ts +0 -116
- package/src/lib/types/conditions.ts +0 -50
- package/src/lib/types/contracts.ts +0 -26
- package/src/lib/types/definitions.ts +0 -77
- package/src/lib/types/index.ts +0 -9
- package/src/lib/types/network.ts +0 -33
- package/src/lib/types/project.ts +0 -9
- package/src/lib/types/source.ts +0 -26
- package/src/lib/types/task.ts +0 -9
- package/src/lib/types/values.ts +0 -221
- package/src/lib/utils/assertion.ts +0 -24
- package/src/lib/utils/validation.ts +0 -116
- package/src/lib/validation/contract-references.ts +0 -210
- package/src/lib/validation/index.ts +0 -1
- package/src/lib/verification/__tests__/etherscan.spec.ts +0 -710
- package/src/lib/verification/__tests__/sourcify.spec.ts +0 -288
- package/src/lib/verification/etherscan.ts +0 -547
- package/src/lib/verification/sourcify.ts +0 -248
- package/test_validation/artifacts/TestContract.json +0 -9
- package/test_validation/jobs/test-missing.yaml +0 -16
- package/test_validation/networks.yaml +0 -3
- package/tsconfig.json +0 -36
package/src/commands/utils.ts
DELETED
|
@@ -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
|
-
})
|