@0xsequence/catapult 1.3.14 → 1.3.16
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/dist/lib/__tests__/deployer.spec.js +22 -0
- package/dist/lib/__tests__/deployer.spec.js.map +1 -1
- package/dist/lib/__tests__/network-loader.spec.js +44 -0
- package/dist/lib/__tests__/network-loader.spec.js.map +1 -1
- package/dist/lib/core/__tests__/context.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/context.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/context.spec.js +25 -0
- package/dist/lib/core/__tests__/context.spec.js.map +1 -0
- package/dist/lib/core/__tests__/resolver.spec.js +40 -0
- package/dist/lib/core/__tests__/resolver.spec.js.map +1 -1
- package/dist/lib/core/context.js +2 -2
- package/dist/lib/core/context.js.map +1 -1
- package/dist/lib/core/resolver.d.ts.map +1 -1
- package/dist/lib/core/resolver.js +23 -8
- package/dist/lib/core/resolver.js.map +1 -1
- package/dist/lib/deployer.d.ts.map +1 -1
- package/dist/lib/deployer.js +2 -1
- package/dist/lib/deployer.js.map +1 -1
- package/dist/lib/network-loader.d.ts.map +1 -1
- package/dist/lib/network-loader.js +4 -1
- package/dist/lib/network-loader.js.map +1 -1
- package/dist/lib/types/network.d.ts +1 -0
- package/dist/lib/types/network.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/lib/__tests__/deployer.spec.ts +28 -0
- package/src/lib/__tests__/network-loader.spec.ts +50 -0
- package/src/lib/core/__tests__/context.spec.ts +37 -0
- package/src/lib/core/__tests__/resolver.spec.ts +42 -0
- package/src/lib/core/context.ts +3 -3
- package/src/lib/core/resolver.ts +28 -10
- package/src/lib/deployer.ts +5 -3
- package/src/lib/network-loader.ts +7 -1
- package/src/lib/types/network.ts +5 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network-loader.js","sourceRoot":"","sources":["../../src/lib/network-loader.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"network-loader.js","sourceRoot":"","sources":["../../src/lib/network-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../../src/lib/types/network.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,OAAO;IAEtB,IAAI,EAAE,MAAM,CAAA;IAGZ,OAAO,EAAE,MAAM,CAAA;IAGf,MAAM,EAAE,MAAM,CAAA;IAGd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IAGnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IAGjB,OAAO,CAAC,EAAE,OAAO,CAAA;IAMjB,UAAU,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../../src/lib/types/network.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,OAAO;IAEtB,IAAI,EAAE,MAAM,CAAA;IAGZ,OAAO,EAAE,MAAM,CAAA;IAGf,MAAM,EAAE,MAAM,CAAA;IAGd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IAGnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IAGjB,OAAO,CAAC,EAAE,OAAO,CAAA;IAMjB,UAAU,CAAC,EAAE,MAAM,CAAA;IAKnB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC"}
|
package/package.json
CHANGED
|
@@ -460,6 +460,34 @@ describe('Deployer', () => {
|
|
|
460
460
|
'deploy2.address': '0xdeploy2'
|
|
461
461
|
})
|
|
462
462
|
})
|
|
463
|
+
|
|
464
|
+
it('should keep outputs empty when only skipped opt-in actions produce no keys', async () => {
|
|
465
|
+
const jobWithSkippedOptIn: Job = {
|
|
466
|
+
name: 'job-skipped-opt-in',
|
|
467
|
+
version: '1.0.0',
|
|
468
|
+
description: 'Job where the only opted-in action produced no outputs',
|
|
469
|
+
actions: [
|
|
470
|
+
{ name: 'deploy-action', template: 'template1', arguments: {}, output: false },
|
|
471
|
+
{ name: 'skipped-action', template: 'template1', arguments: {}, output: true }
|
|
472
|
+
]
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
mockLoader.jobs.clear()
|
|
476
|
+
mockLoader.jobs.set('job-skipped-opt-in', jobWithSkippedOptIn)
|
|
477
|
+
mockGraph.getExecutionOrder.mockReturnValue(['job-skipped-opt-in'])
|
|
478
|
+
|
|
479
|
+
mockContext.getOutputs.mockReturnValue(new Map<string, any>([
|
|
480
|
+
['deploy-action.address', '0xdeployaddress']
|
|
481
|
+
]))
|
|
482
|
+
|
|
483
|
+
const deployer = new Deployer(deployerOptions)
|
|
484
|
+
await deployer.run()
|
|
485
|
+
|
|
486
|
+
const outputCall = mockFs.writeFile.mock.calls[0]
|
|
487
|
+
const outputContent = JSON.parse(outputCall[1] as string)
|
|
488
|
+
|
|
489
|
+
expect(outputContent.networks[0].outputs).toEqual({})
|
|
490
|
+
})
|
|
463
491
|
})
|
|
464
492
|
|
|
465
493
|
describe('error handling', () => {
|
|
@@ -98,3 +98,53 @@ describe('network-loader rpcUrl token replacement', () => {
|
|
|
98
98
|
expect(networks[0].rpcUrl).toBe('https://node.example.com/')
|
|
99
99
|
})
|
|
100
100
|
})
|
|
101
|
+
|
|
102
|
+
describe('network-loader params', () => {
|
|
103
|
+
afterAll(async () => {
|
|
104
|
+
try {
|
|
105
|
+
await fs.rm(tmpDir, { recursive: true, force: true })
|
|
106
|
+
} catch {}
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test('passes through valid params object', async () => {
|
|
110
|
+
const projectRoot = path.join(tmpDir, 'params-valid')
|
|
111
|
+
const yaml = `
|
|
112
|
+
- name: "MyNet"
|
|
113
|
+
chainId: 4217
|
|
114
|
+
rpcUrl: "http://127.0.0.1:8545"
|
|
115
|
+
params:
|
|
116
|
+
myParam: true
|
|
117
|
+
`
|
|
118
|
+
await writeNetworksYaml(projectRoot, yaml)
|
|
119
|
+
|
|
120
|
+
const networks = await loadNetworks(projectRoot)
|
|
121
|
+
expect(networks).toHaveLength(1)
|
|
122
|
+
expect(networks[0].params).toEqual({ myParam: true })
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test('rejects params when not a plain object', async () => {
|
|
126
|
+
const projectRoot = path.join(tmpDir, 'params-invalid-array')
|
|
127
|
+
const yaml = `
|
|
128
|
+
- name: "BadNet"
|
|
129
|
+
chainId: 1
|
|
130
|
+
rpcUrl: "http://127.0.0.1:8545"
|
|
131
|
+
params: [1, 2, 3]
|
|
132
|
+
`
|
|
133
|
+
await writeNetworksYaml(projectRoot, yaml)
|
|
134
|
+
|
|
135
|
+
await expect(loadNetworks(projectRoot)).rejects.toThrow(/Failed to load or parse networks.yaml/)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
test('rejects params when null', async () => {
|
|
139
|
+
const projectRoot = path.join(tmpDir, 'params-invalid-null')
|
|
140
|
+
const yaml = `
|
|
141
|
+
- name: "BadNet"
|
|
142
|
+
chainId: 1
|
|
143
|
+
rpcUrl: "http://127.0.0.1:8545"
|
|
144
|
+
params: null
|
|
145
|
+
`
|
|
146
|
+
await writeNetworksYaml(projectRoot, yaml)
|
|
147
|
+
|
|
148
|
+
await expect(loadNetworks(projectRoot)).rejects.toThrow(/Failed to load or parse networks.yaml/)
|
|
149
|
+
})
|
|
150
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ethers } from 'ethers'
|
|
2
|
+
import { ExecutionContext } from '../context'
|
|
3
|
+
import { Network } from '../../types'
|
|
4
|
+
|
|
5
|
+
describe('ExecutionContext', () => {
|
|
6
|
+
const network: Network = {
|
|
7
|
+
name: 'test',
|
|
8
|
+
chainId: 31337,
|
|
9
|
+
rpcUrl: 'http://127.0.0.1:8545'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const contractRepository = {} as any
|
|
13
|
+
|
|
14
|
+
it('wraps private-key signers in a NonceManager', async () => {
|
|
15
|
+
const context = new ExecutionContext(
|
|
16
|
+
network,
|
|
17
|
+
'0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
|
|
18
|
+
contractRepository
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
const signer = await context.getResolvedSigner()
|
|
22
|
+
expect(signer).toBeInstanceOf(ethers.NonceManager)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('wraps promised signers in a NonceManager when first resolved', async () => {
|
|
26
|
+
const getSignerSpy = jest.spyOn(ethers.JsonRpcProvider.prototype, 'getSigner')
|
|
27
|
+
getSignerSpy.mockResolvedValue(
|
|
28
|
+
ethers.Wallet.createRandom().connect(new ethers.JsonRpcProvider(network.rpcUrl)) as any
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
const context = new ExecutionContext(network, undefined, contractRepository)
|
|
32
|
+
|
|
33
|
+
await expect(context.getResolvedSigner()).resolves.toBeInstanceOf(ethers.NonceManager)
|
|
34
|
+
|
|
35
|
+
getSignerSpy.mockRestore()
|
|
36
|
+
})
|
|
37
|
+
})
|
|
@@ -1573,6 +1573,48 @@ describe('ValueResolver', () => {
|
|
|
1573
1573
|
})
|
|
1574
1574
|
})
|
|
1575
1575
|
|
|
1576
|
+
describe('Network().params dotted paths', () => {
|
|
1577
|
+
const mockPrivateKey = '0x0000000000000000000000000000000000000000000000000000000000000001'
|
|
1578
|
+
|
|
1579
|
+
it('should default params.myParam to false when params is absent', async () => {
|
|
1580
|
+
const result = await resolver.resolve('{{Network().params.myParam}}', context)
|
|
1581
|
+
expect(result).toBe(false)
|
|
1582
|
+
})
|
|
1583
|
+
|
|
1584
|
+
it('should default params.myParam to false when myParam is absent', async () => {
|
|
1585
|
+
const networkWithParams: Network = { ...mockNetwork, params: {} }
|
|
1586
|
+
const paramsContext = new ExecutionContext(networkWithParams, mockPrivateKey, mockRegistry)
|
|
1587
|
+
try {
|
|
1588
|
+
const result = await resolver.resolve('{{Network().params.myParam}}', paramsContext)
|
|
1589
|
+
expect(result).toBe(false)
|
|
1590
|
+
} finally {
|
|
1591
|
+
await paramsContext.dispose()
|
|
1592
|
+
}
|
|
1593
|
+
})
|
|
1594
|
+
|
|
1595
|
+
it('should return params.myParam when set to true', async () => {
|
|
1596
|
+
const networkWithParams: Network = { ...mockNetwork, params: { myParam: true } }
|
|
1597
|
+
const paramsContext = new ExecutionContext(networkWithParams, mockPrivateKey, mockRegistry)
|
|
1598
|
+
try {
|
|
1599
|
+
const result = await resolver.resolve('{{Network().params.myParam}}', paramsContext)
|
|
1600
|
+
expect(result).toBe(true)
|
|
1601
|
+
} finally {
|
|
1602
|
+
await paramsContext.dispose()
|
|
1603
|
+
}
|
|
1604
|
+
})
|
|
1605
|
+
|
|
1606
|
+
it('should return params.myParam when set to false', async () => {
|
|
1607
|
+
const networkWithParams: Network = { ...mockNetwork, params: { myParam: false } }
|
|
1608
|
+
const paramsContext = new ExecutionContext(networkWithParams, mockPrivateKey, mockRegistry)
|
|
1609
|
+
try {
|
|
1610
|
+
const result = await resolver.resolve('{{Network().params.myParam}}', paramsContext)
|
|
1611
|
+
expect(result).toBe(false)
|
|
1612
|
+
} finally {
|
|
1613
|
+
await paramsContext.dispose()
|
|
1614
|
+
}
|
|
1615
|
+
})
|
|
1616
|
+
})
|
|
1617
|
+
|
|
1576
1618
|
describe('invalid Network expressions', () => {
|
|
1577
1619
|
it('should fail for invalid property', async () => {
|
|
1578
1620
|
await expect(resolver.resolve('{{Network().invalid}}', context))
|
package/src/lib/core/context.ts
CHANGED
|
@@ -33,11 +33,11 @@ export class ExecutionContext {
|
|
|
33
33
|
|
|
34
34
|
// Determine the signer
|
|
35
35
|
if (privateKey) {
|
|
36
|
-
this.signer = new ethers.Wallet(privateKey, this.provider)
|
|
36
|
+
this.signer = new ethers.NonceManager(new ethers.Wallet(privateKey, this.provider))
|
|
37
37
|
} else if (network.rpcUrl) {
|
|
38
38
|
// If no private key, but RPC URL is provided, get a signer from the provider.
|
|
39
39
|
// This returns a Promise that we need to resolve on first use.
|
|
40
|
-
this.signer = this.provider.getSigner() // Keep as Promise
|
|
40
|
+
this.signer = this.provider.getSigner().then(signer => new ethers.NonceManager(signer)) // Keep as Promise
|
|
41
41
|
} else {
|
|
42
42
|
throw new Error('A private key must be provided or an RPC URL must be configured to obtain a signer for the network.')
|
|
43
43
|
}
|
|
@@ -124,4 +124,4 @@ export class ExecutionContext {
|
|
|
124
124
|
// Ignore errors during cleanup
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
|
-
}
|
|
127
|
+
}
|
package/src/lib/core/resolver.ts
CHANGED
|
@@ -118,20 +118,38 @@ export class ValueResolver {
|
|
|
118
118
|
return value
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
// Check for Network(
|
|
122
|
-
const networkMatch = expression.match(/^Network\(\)\.(
|
|
121
|
+
// Check for Network() property access (single segment or dotted path)
|
|
122
|
+
const networkMatch = expression.match(/^Network\(\)\.(.+)$/)
|
|
123
123
|
if (networkMatch) {
|
|
124
|
-
const
|
|
124
|
+
const path = networkMatch[1]
|
|
125
|
+
const segments = path.split('.')
|
|
125
126
|
const network = context.getNetwork()
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
|
|
128
|
+
if (segments.length === 1) {
|
|
129
|
+
const property = segments[0]
|
|
130
|
+
if (property === 'testnet') {
|
|
131
|
+
// Default to false if not set.
|
|
132
|
+
return !!network.testnet
|
|
133
|
+
}
|
|
134
|
+
const value = (network as unknown as Record<string, unknown>)[property]
|
|
135
|
+
if (value === undefined) {
|
|
136
|
+
throw new Error(`Property "${property}" does not exist on network`)
|
|
137
|
+
}
|
|
138
|
+
return value
|
|
129
139
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
140
|
+
|
|
141
|
+
// Dotted paths (e.g. params.myParam): missing keys default to false.
|
|
142
|
+
let current: unknown = network
|
|
143
|
+
for (const segment of segments) {
|
|
144
|
+
if (current === null || current === undefined || typeof current !== 'object') {
|
|
145
|
+
return false
|
|
146
|
+
}
|
|
147
|
+
current = (current as Record<string, unknown>)[segment]
|
|
148
|
+
if (current === undefined) {
|
|
149
|
+
return false
|
|
150
|
+
}
|
|
133
151
|
}
|
|
134
|
-
return
|
|
152
|
+
return current
|
|
135
153
|
}
|
|
136
154
|
|
|
137
155
|
// Check scope for local variables (template arguments) first
|
package/src/lib/deployer.ts
CHANGED
|
@@ -879,8 +879,10 @@ export class Deployer {
|
|
|
879
879
|
|
|
880
880
|
// 3) Exclude any actions explicitly marked false (they won't be included by rules above anyway)
|
|
881
881
|
|
|
882
|
-
|
|
883
|
-
|
|
882
|
+
const hasExplicitOutputSelection = actionsWithCustomMap.length > 0 || actionsWithTrue.length > 0
|
|
883
|
+
|
|
884
|
+
// If the job has any explicit output selection, return the selected outputs even when empty.
|
|
885
|
+
if (hasExplicitOutputSelection) {
|
|
884
886
|
// Additionally, filter out any accidentally included outputs from actions marked false
|
|
885
887
|
for (const falseActionName of actionsWithFalse) {
|
|
886
888
|
for (const key of Array.from(result.keys())) {
|
|
@@ -976,4 +978,4 @@ export class Deployer {
|
|
|
976
978
|
// Return all entries: successes first, then errors
|
|
977
979
|
return [...successEntries, ...errorEntries]
|
|
978
980
|
}
|
|
979
|
-
}
|
|
981
|
+
}
|
|
@@ -22,7 +22,13 @@ function isValidNetwork(obj: unknown): obj is Network {
|
|
|
22
22
|
// testnet field is optional and should be a boolean if present
|
|
23
23
|
(!('testnet' in obj) || typeof (obj as Record<string, unknown>).testnet === 'boolean') &&
|
|
24
24
|
// evmVersion field is optional and should be a string if present
|
|
25
|
-
(!('evmVersion' in obj) || typeof (obj as Record<string, unknown>).evmVersion === 'string')
|
|
25
|
+
(!('evmVersion' in obj) || typeof (obj as Record<string, unknown>).evmVersion === 'string') &&
|
|
26
|
+
// params field is optional and must be a plain object if present
|
|
27
|
+
(!('params' in obj) || (
|
|
28
|
+
typeof (obj as Record<string, unknown>).params === 'object' &&
|
|
29
|
+
(obj as Record<string, unknown>).params !== null &&
|
|
30
|
+
!Array.isArray((obj as Record<string, unknown>).params)
|
|
31
|
+
))
|
|
26
32
|
)
|
|
27
33
|
}
|
|
28
34
|
|
package/src/lib/types/network.ts
CHANGED
|
@@ -25,4 +25,9 @@ export interface Network {
|
|
|
25
25
|
* "paris", "shanghai", "cancun". Used to filter jobs that require a minimum EVM version.
|
|
26
26
|
*/
|
|
27
27
|
evmVersion?: string
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Integrator-owned metadata bag. Keys are not validated by Catapult.
|
|
31
|
+
*/
|
|
32
|
+
params?: Record<string, unknown>
|
|
28
33
|
}
|