@0xsequence/catapult 1.3.9 → 1.3.11

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 (32) hide show
  1. package/README.md +18 -0
  2. package/dist/lib/core/__tests__/json-integration.spec.js +22 -0
  3. package/dist/lib/core/__tests__/json-integration.spec.js.map +1 -1
  4. package/dist/lib/core/__tests__/resolver.spec.js +58 -0
  5. package/dist/lib/core/__tests__/resolver.spec.js.map +1 -1
  6. package/dist/lib/core/loader.d.ts.map +1 -1
  7. package/dist/lib/core/loader.js +0 -14
  8. package/dist/lib/core/loader.js.map +1 -1
  9. package/dist/lib/core/resolver.d.ts +4 -0
  10. package/dist/lib/core/resolver.d.ts.map +1 -1
  11. package/dist/lib/core/resolver.js +88 -0
  12. package/dist/lib/core/resolver.js.map +1 -1
  13. package/dist/lib/parsers/__tests__/job.spec.js +25 -2
  14. package/dist/lib/parsers/__tests__/job.spec.js.map +1 -1
  15. package/dist/lib/parsers/job.d.ts.map +1 -1
  16. package/dist/lib/parsers/job.js +31 -1
  17. package/dist/lib/parsers/job.js.map +1 -1
  18. package/dist/lib/std/templates/create4.yaml +172 -0
  19. package/dist/lib/types/values.d.ts +10 -1
  20. package/dist/lib/types/values.d.ts.map +1 -1
  21. package/dist/lib/utils/create4.d.ts +78 -0
  22. package/dist/lib/utils/create4.d.ts.map +1 -0
  23. package/dist/lib/utils/create4.js +237 -0
  24. package/dist/lib/utils/create4.js.map +1 -0
  25. package/package.json +13 -12
  26. package/src/lib/core/__tests__/json-integration.spec.ts +27 -2
  27. package/src/lib/core/__tests__/resolver.spec.ts +70 -2
  28. package/src/lib/core/loader.ts +0 -16
  29. package/src/lib/core/resolver.ts +114 -1
  30. package/src/lib/parsers/__tests__/job.spec.ts +27 -4
  31. package/src/lib/parsers/job.ts +44 -2
  32. package/src/lib/types/values.ts +12 -1
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.computeCreate4Plan = computeCreate4Plan;
4
+ const ethers_1 = require("ethers");
5
+ const create4_1 = require("@0xsequence/create4");
6
+ function computeCreate4Plan(args) {
7
+ const factoryAddress = normalizeAddress(args.deployerAddress);
8
+ const targetChainId = normalizeChainIdInput(args.chainId, 'chain id');
9
+ const planSalt = normalizePlanSalt(args.salt);
10
+ const planSpec = buildPlanSpec(args.bytecodes, planSalt);
11
+ const plan = (0, create4_1.buildPlanFromSpec)(planSpec);
12
+ const deploymentSalt = (0, create4_1.deriveDeploymentSalt)(plan.root, planSalt);
13
+ const address = (0, create4_1.computeCreate3Address)(factoryAddress, deploymentSalt);
14
+ const targetLeaf = plan.leaves.find(leaf => BigInt(leaf.chainId) === targetChainId);
15
+ const target = targetLeaf
16
+ ? buildChainTarget(targetLeaf)
17
+ : buildFallbackTarget(plan.leaves, plan.fallback, targetChainId);
18
+ return {
19
+ factoryAddress,
20
+ chainId: targetChainId.toString(),
21
+ planRoot: plan.root,
22
+ salt: plan.salt,
23
+ deploymentSalt,
24
+ address,
25
+ target,
26
+ plan: {
27
+ root: plan.root,
28
+ leaves: plan.leaves,
29
+ fallback: plan.fallback,
30
+ },
31
+ };
32
+ }
33
+ function buildPlanSpec(bytecodes, salt) {
34
+ if (!bytecodes || typeof bytecodes !== 'object') {
35
+ throw new Error('create4-plan: bytecodes must be an object with chain entries and a fallback');
36
+ }
37
+ const sharedConstructor = normalizeConstructor(bytecodes.sharedConstructor, 'bytecodes.sharedConstructor');
38
+ const fallbackEntry = bytecodes.fallback ?? bytecodes['fallbackInitCode'];
39
+ if (!fallbackEntry) {
40
+ throw new Error('create4-plan: bytecodes.fallback is required');
41
+ }
42
+ const chains = [];
43
+ const chainSource = (bytecodes.chains && typeof bytecodes.chains === 'object') ? bytecodes.chains : bytecodes;
44
+ for (const [key, value] of Object.entries(chainSource)) {
45
+ if (key === 'fallback' || key === 'fallbackInitCode' || key === 'sharedConstructor' || key === 'chains') {
46
+ continue;
47
+ }
48
+ if (value === undefined || value === null) {
49
+ throw new Error(`create4-plan: bytecodes entry "${key}" is missing a value`);
50
+ }
51
+ chains.push(normalizeChainEntry(key, value, sharedConstructor));
52
+ }
53
+ if (chains.length === 0) {
54
+ throw new Error('create4-plan: at least one chain entry is required');
55
+ }
56
+ return {
57
+ salt,
58
+ fallbackInitCode: normalizeBytecodeEntry(fallbackEntry, undefined, 'bytecodes.fallback'),
59
+ chains: chains.map(entry => ({
60
+ chainId: entry.chainId.toString(),
61
+ initCode: entry.initCode,
62
+ label: entry.label,
63
+ })),
64
+ };
65
+ }
66
+ function buildChainTarget(leaf) {
67
+ return {
68
+ mode: 'chain',
69
+ modeFlag: '1',
70
+ chainId: leaf.chainId,
71
+ nextChainId: leaf.nextChainId,
72
+ initCode: leaf.initCode,
73
+ initCodeHash: leaf.initCodeHash,
74
+ proof: leaf.proof,
75
+ };
76
+ }
77
+ function buildFallbackTarget(leaves, fallback, targetChainId) {
78
+ const gapLeaf = findGapLeaf(leaves, targetChainId);
79
+ return {
80
+ mode: 'fallback',
81
+ modeFlag: '0',
82
+ gapChainId: gapLeaf.chainId,
83
+ gapNextChainId: gapLeaf.nextChainId,
84
+ gapLeafPrefix: gapLeaf.prefix,
85
+ gapLeafHash: gapLeaf.initCodeHash,
86
+ gapProof: gapLeaf.proof,
87
+ initCode: fallback.initCode,
88
+ initCodeHash: fallback.initCodeHash,
89
+ proof: fallback.proof,
90
+ };
91
+ }
92
+ function findGapLeaf(leaves, targetChainId) {
93
+ for (const leaf of leaves) {
94
+ if ((0, create4_1.isChainIdInGap)(leaf.chainId, leaf.nextChainId, targetChainId)) {
95
+ return leaf;
96
+ }
97
+ }
98
+ throw new Error(`create4-plan: no fallback gap covers chain id ${targetChainId.toString()}`);
99
+ }
100
+ function normalizePlanSalt(value) {
101
+ if (value === undefined || value === null || value === '') {
102
+ return create4_1.ZERO_SALT;
103
+ }
104
+ return (0, create4_1.normalizeSaltHex)(value);
105
+ }
106
+ function normalizeChainEntry(key, entry, sharedConstructor) {
107
+ const trimmedKey = key.trim();
108
+ if (!trimmedKey) {
109
+ throw new Error('create4-plan: chain entry keys cannot be empty');
110
+ }
111
+ let entryObj;
112
+ if (typeof entry === 'string') {
113
+ entryObj = { initCode: entry };
114
+ }
115
+ else if (entry && typeof entry === 'object') {
116
+ entryObj = entry;
117
+ }
118
+ else {
119
+ throw new Error(`create4-plan: chain entry "${key}" must be a hex string or an object`);
120
+ }
121
+ const keyChainId = normalizeChainIdInput(trimmedKey, `chain id key "${key}"`);
122
+ if (entryObj.chainId !== undefined) {
123
+ const providedChainId = normalizeChainIdInput(entryObj.chainId, `chain id for "${key}"`);
124
+ if (providedChainId !== keyChainId) {
125
+ throw new Error(`create4-plan: chain id mismatch for entry "${key}"`);
126
+ }
127
+ }
128
+ return {
129
+ key,
130
+ chainId: keyChainId,
131
+ label: typeof entryObj.label === 'string' ? entryObj.label : undefined,
132
+ initCode: normalizeBytecodeEntry(entryObj, sharedConstructor, `bytecodes[${key}]`),
133
+ };
134
+ }
135
+ function normalizeBytecodeEntry(entry, sharedConstructor, context) {
136
+ if (typeof entry === 'string') {
137
+ return normalizeHex(entry, `${context}.initCode`);
138
+ }
139
+ if (!entry || typeof entry !== 'object') {
140
+ throw new Error(`create4-plan: ${context} must be a hex string or an object`);
141
+ }
142
+ if (entry.initCode !== undefined) {
143
+ return normalizeHex(entry.initCode, `${context}.initCode`);
144
+ }
145
+ if (!entry.creationCode) {
146
+ throw new Error(`create4-plan: ${context} must include initCode or creationCode`);
147
+ }
148
+ const creationCode = normalizeHex(entry.creationCode, `${context}.creationCode`);
149
+ const hasOwnConstructor = Object.prototype.hasOwnProperty.call(entry, 'constructor');
150
+ const constructorOverride = hasOwnConstructor ? entry.constructor : undefined;
151
+ const constructorSpec = normalizeConstructor(constructorOverride ?? sharedConstructor, `${context}.constructor`);
152
+ if (!constructorSpec) {
153
+ return creationCode;
154
+ }
155
+ const encodedArgs = encodeConstructorArgs(constructorSpec, context);
156
+ return ethers_1.ethers.hexlify(ethers_1.ethers.concat([ethers_1.ethers.getBytes(creationCode), ethers_1.ethers.getBytes(encodedArgs)]));
157
+ }
158
+ function normalizeConstructor(value, context) {
159
+ if (value === undefined || value === null) {
160
+ return undefined;
161
+ }
162
+ if (typeof value !== 'object') {
163
+ throw new Error(`create4-plan: ${context} must be an object with types and values arrays`);
164
+ }
165
+ const types = Array.isArray(value.types) ? value.types.map(t => String(t)) : undefined;
166
+ const values = Array.isArray(value.values) ? [...value.values] : undefined;
167
+ if (!types || !values) {
168
+ throw new Error(`create4-plan: ${context} must include types[] and values[]`);
169
+ }
170
+ if (types.length !== values.length) {
171
+ throw new Error(`create4-plan: ${context} types length (${types.length}) must match values length (${values.length})`);
172
+ }
173
+ return { types, values };
174
+ }
175
+ function encodeConstructorArgs(constructorSpec, context) {
176
+ try {
177
+ return ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(constructorSpec.types, constructorSpec.values);
178
+ }
179
+ catch (error) {
180
+ const reason = error instanceof Error ? error.message : String(error);
181
+ throw new Error(`create4-plan: failed to encode constructor for ${context}: ${reason}`);
182
+ }
183
+ }
184
+ function normalizeHex(value, label) {
185
+ if (typeof value !== 'string') {
186
+ throw new Error(`create4-plan: ${label} must be provided as a hex string`);
187
+ }
188
+ let hex = value.trim();
189
+ if (!hex.startsWith('0x')) {
190
+ hex = '0x' + hex;
191
+ }
192
+ try {
193
+ return ethers_1.ethers.hexlify(ethers_1.ethers.getBytes(hex));
194
+ }
195
+ catch {
196
+ throw new Error(`create4-plan: ${label} must be a valid hex string`);
197
+ }
198
+ }
199
+ function normalizeChainIdInput(value, label) {
200
+ if (typeof value === 'bigint') {
201
+ return assertChainIdRange(value, label);
202
+ }
203
+ if (typeof value === 'number') {
204
+ if (!Number.isFinite(value) || !Number.isInteger(value)) {
205
+ throw new Error(`create4-plan: ${label} must be a finite integer`);
206
+ }
207
+ return assertChainIdRange(BigInt(value), label);
208
+ }
209
+ if (typeof value === 'string') {
210
+ const trimmed = value.trim();
211
+ if (!trimmed) {
212
+ throw new Error(`create4-plan: ${label} cannot be empty`);
213
+ }
214
+ try {
215
+ return assertChainIdRange(BigInt(trimmed), label);
216
+ }
217
+ catch {
218
+ throw new Error(`create4-plan: invalid ${label}: ${value}`);
219
+ }
220
+ }
221
+ throw new Error(`create4-plan: ${label} must be a string, number, or bigint`);
222
+ }
223
+ function assertChainIdRange(value, label) {
224
+ if (value < 0n || value > ((1n << 64n) - 1n)) {
225
+ throw new Error(`create4-plan: ${label} must fit within uint64 range`);
226
+ }
227
+ return value;
228
+ }
229
+ function normalizeAddress(value) {
230
+ try {
231
+ return ethers_1.ethers.getAddress(value);
232
+ }
233
+ catch {
234
+ throw new Error(`create4-plan: invalid deployer address: ${value}`);
235
+ }
236
+ }
237
+ //# sourceMappingURL=create4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create4.js","sourceRoot":"","sources":["../../../src/lib/utils/create4.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,11 +1,23 @@
1
1
  {
2
2
  "name": "@0xsequence/catapult",
3
- "version": "1.3.9",
3
+ "version": "1.3.11",
4
4
  "description": "Ethereum contract deployment CLI tool",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "catapult": "dist/index.js"
8
8
  },
9
+ "scripts": {
10
+ "build": "tsc && cp -r src/lib/std dist/lib/",
11
+ "dev": "ts-node src/index.ts",
12
+ "start": "node dist/index.js",
13
+ "start:dev": "npm run build && npm run start",
14
+ "watch": "tsc --watch",
15
+ "clean": "rm -rf dist",
16
+ "prepare": "npm run build",
17
+ "test": "jest --runInBand --detectOpenHandles",
18
+ "lint": "eslint src/**/*.ts",
19
+ "lint:fix": "eslint src/**/*.ts --fix"
20
+ },
9
21
  "keywords": [
10
22
  "ethereum",
11
23
  "deployment",
@@ -52,16 +64,5 @@
52
64
  },
53
65
  "engines": {
54
66
  "node": ">=16.0.0"
55
- },
56
- "scripts": {
57
- "build": "tsc && cp -r src/lib/std dist/lib/",
58
- "dev": "ts-node src/index.ts",
59
- "start": "node dist/index.js",
60
- "start:dev": "npm run build && npm run start",
61
- "watch": "tsc --watch",
62
- "clean": "rm -rf dist",
63
- "test": "jest --runInBand --detectOpenHandles",
64
- "lint": "eslint src/**/*.ts",
65
- "lint:fix": "eslint src/**/*.ts --fix"
66
67
  }
67
68
  }
@@ -2,7 +2,7 @@ import { ExecutionEngine } from '../engine'
2
2
  import { ExecutionContext } from '../context'
3
3
  import { ValueResolver } from '../resolver'
4
4
  import { ContractRepository } from '../../contracts/repository'
5
- import { Action, Network, ReadJsonValue } from '../../types'
5
+ import { Action, Network, ReadJsonValue, SliceBytesValue } from '../../types'
6
6
  import { VerificationPlatformRegistry } from '../../verification/etherscan'
7
7
 
8
8
  describe('JSON Integration Tests', () => {
@@ -292,6 +292,31 @@ describe('JSON Integration Tests', () => {
292
292
  expect(extractedTo).toBe('0x596aF90CecdBF9A768886E771178fd5561dD27Ab')
293
293
  })
294
294
 
295
+ it('should allow piping read-json output into slice-bytes', async () => {
296
+ const response = {
297
+ txs: {
298
+ data: '0xaabbccddff'
299
+ }
300
+ }
301
+
302
+ const value: SliceBytesValue = {
303
+ type: 'slice-bytes',
304
+ arguments: {
305
+ value: {
306
+ type: 'read-json',
307
+ arguments: {
308
+ json: response,
309
+ path: 'txs.data'
310
+ }
311
+ },
312
+ range: ':-1'
313
+ }
314
+ }
315
+
316
+ const trimmed = await resolver.resolve(value, context)
317
+ expect(trimmed).toBe('0xaabbccdd')
318
+ })
319
+
295
320
  it('should handle complex nested API responses', async () => {
296
321
  const complexApiResponse = {
297
322
  status: 'success',
@@ -357,4 +382,4 @@ describe('JSON Integration Tests', () => {
357
382
  expect(await resolver.resolve(timestamp, context)).toBe(1234567890)
358
383
  })
359
384
  })
360
- })
385
+ })
@@ -1,7 +1,7 @@
1
1
  import { ethers } from 'ethers'
2
2
  import { ValueResolver } from '../resolver'
3
3
  import { ExecutionContext } from '../context'
4
- import { BasicArithmeticValue, Network, ReadBalanceValue, ComputeCreate2Value, ConstructorEncodeValue, AbiEncodeValue, AbiPackValue, CallValue, ContractExistsValue, ComputeCreateValue } from '../../types'
4
+ import { BasicArithmeticValue, Network, ReadBalanceValue, ComputeCreate2Value, ConstructorEncodeValue, AbiEncodeValue, AbiPackValue, CallValue, ContractExistsValue, ComputeCreateValue, SliceBytesValue } from '../../types'
5
5
  import { ContractRepository } from '../../contracts/repository'
6
6
 
7
7
  describe('ValueResolver', () => {
@@ -1918,4 +1918,72 @@ describe('ValueResolver', () => {
1918
1918
  await expect(resolver.resolve(value, context)).rejects.toThrow('contract-exists: invalid address: invalid-address')
1919
1919
  })
1920
1920
  })
1921
- })
1921
+
1922
+ describe('slice-bytes', () => {
1923
+ it('should slice bytes with explicit start and end positions', async () => {
1924
+ const value: SliceBytesValue = {
1925
+ type: 'slice-bytes',
1926
+ arguments: {
1927
+ value: '0x112233445566',
1928
+ start: 1,
1929
+ end: 4,
1930
+ },
1931
+ }
1932
+
1933
+ const result = await resolver.resolve(value, context)
1934
+ expect(result).toBe('0x223344')
1935
+ })
1936
+
1937
+ it('should support negative indexes to drop trailing bytes', async () => {
1938
+ const value: SliceBytesValue = {
1939
+ type: 'slice-bytes',
1940
+ arguments: {
1941
+ value: '0xdeadbeefcafebabe',
1942
+ end: -1,
1943
+ },
1944
+ }
1945
+
1946
+ const result = await resolver.resolve(value, context)
1947
+ expect(result).toBe('0xdeadbeefcafeba')
1948
+ })
1949
+
1950
+ it('should accept range syntax', async () => {
1951
+ const value: SliceBytesValue = {
1952
+ type: 'slice-bytes',
1953
+ arguments: {
1954
+ value: '0xaabbccddeeff',
1955
+ range: '0:2',
1956
+ },
1957
+ }
1958
+
1959
+ const result = await resolver.resolve(value, context)
1960
+ expect(result).toBe('0xaabb')
1961
+ })
1962
+
1963
+ it('should handle open range syntax with missing start', async () => {
1964
+ const value: SliceBytesValue = {
1965
+ type: 'slice-bytes',
1966
+ arguments: {
1967
+ value: '0xaabbccddeeff',
1968
+ range: ':-1',
1969
+ },
1970
+ }
1971
+
1972
+ const result = await resolver.resolve(value, context)
1973
+ expect(result).toBe('0xaabbccddee')
1974
+ })
1975
+
1976
+ it('should accept bracketed range syntax', async () => {
1977
+ const value: SliceBytesValue = {
1978
+ type: 'slice-bytes',
1979
+ arguments: {
1980
+ value: '0x0102030405',
1981
+ range: '[:3]',
1982
+ },
1983
+ }
1984
+
1985
+ const result = await resolver.resolve(value, context)
1986
+ expect(result).toBe('0x010203')
1987
+ })
1988
+ })
1989
+ })
@@ -121,22 +121,6 @@ export class ProjectLoader {
121
121
  }
122
122
 
123
123
  const job = parseJob(content)
124
- // Capture optional job-level constants by peeking the raw YAML for "constants"
125
- try {
126
- const raw = JSON.parse(JSON.stringify(require('yaml').parse(content)))
127
- if (raw && typeof raw === 'object' && raw.constants !== undefined) {
128
- if (typeof raw.constants !== 'object' || Array.isArray(raw.constants)) {
129
- throw new Error(`Invalid job "${job.name}": "constants" field must be an object if provided.`)
130
- }
131
- (job as any).constants = raw.constants
132
- }
133
- } catch (peekErr) {
134
- // parseJob already validated YAML; if this peek fails due to YAML parsing, surface it
135
- if (peekErr instanceof Error && peekErr.message.includes('Failed to parse')) {
136
- throw new Error(`Job constants peek failed for ${filePath}: ${peekErr.message}`)
137
- }
138
- // Otherwise ignore non-YAML related errors here
139
- }
140
124
  job._path = filePath
141
125
  this.jobs.set(job.name, job)
142
126
  } catch (error) {
@@ -13,6 +13,7 @@ import {
13
13
  ContractExistsValue,
14
14
  JobCompletedValue,
15
15
  ReadJsonValue,
16
+ SliceBytesValue,
16
17
  } from '../types'
17
18
  import { ExecutionContext } from './context'
18
19
  import { isAddress, isBigNumberish, isBytesLike } from '../utils/assertion'
@@ -189,6 +190,8 @@ export class ValueResolver {
189
190
  return this.resolveReadJson(resolvedArgs as ReadJsonValue['arguments'])
190
191
  case 'resolve-json':
191
192
  return this.resolveJsonValue(resolvedArgs, context)
193
+ case 'slice-bytes':
194
+ return this.resolveSliceBytes(resolvedArgs as SliceBytesValue['arguments'])
192
195
  default:
193
196
  throw new Error(`Unknown value resolver type: ${(obj as any).type}`)
194
197
  }
@@ -530,6 +533,116 @@ export class ValueResolver {
530
533
  }
531
534
  }
532
535
 
536
+ private resolveSliceBytes(args: SliceBytesValue['arguments']): string {
537
+ const { value, start, end, range } = args
538
+
539
+ if (!isBytesLike(value)) {
540
+ throw new Error('slice-bytes: value must be bytes-like (hex string, Uint8Array, etc.)')
541
+ }
542
+
543
+ if (range !== undefined && (start !== undefined || end !== undefined)) {
544
+ throw new Error('slice-bytes: provide either range or start/end, not both')
545
+ }
546
+
547
+ if (range !== undefined && typeof range !== 'string') {
548
+ throw new Error('slice-bytes: range must be a string in "start:end" format')
549
+ }
550
+
551
+ const normalizedHex = ethers.hexlify(value as ethers.BytesLike)
552
+ const hexBody = normalizedHex.slice(2)
553
+
554
+ if (hexBody.length % 2 !== 0) {
555
+ throw new Error('slice-bytes: value must have an even-length hex string')
556
+ }
557
+
558
+ const totalBytes = hexBody.length / 2
559
+ const { startIndex, endIndex } = this.computeSliceBounds(totalBytes, { start, end, range })
560
+
561
+ if (startIndex >= endIndex) {
562
+ return '0x'
563
+ }
564
+
565
+ const sliced = hexBody.slice(startIndex * 2, endIndex * 2)
566
+ return sliced.length === 0 ? '0x' : `0x${sliced}`
567
+ }
568
+
569
+ private computeSliceBounds(
570
+ totalBytes: number,
571
+ params: { start?: any; end?: any; range?: any },
572
+ ): { startIndex: number; endIndex: number } {
573
+ let startValue: number | undefined
574
+ let endValue: number | undefined
575
+
576
+ if (params.range !== undefined) {
577
+ const trimmedRange = (params.range as string).trim()
578
+ const rangeMatch = trimmedRange.match(/^\[?\s*(-?\d+)?\s*:\s*(-?\d+)?\s*\]?$/)
579
+
580
+ if (!rangeMatch) {
581
+ throw new Error('slice-bytes: range must follow the "start:end" format (e.g., "0:4" or ":-1")')
582
+ }
583
+
584
+ const [, rawStart, rawEnd] = rangeMatch
585
+ if (rawStart !== undefined) {
586
+ startValue = this.parseSliceIndex(rawStart, 'range start')
587
+ }
588
+ if (rawEnd !== undefined) {
589
+ endValue = this.parseSliceIndex(rawEnd, 'range end')
590
+ }
591
+ } else {
592
+ if (params.start !== undefined) {
593
+ startValue = this.parseSliceIndex(params.start, 'start')
594
+ }
595
+ if (params.end !== undefined) {
596
+ endValue = this.parseSliceIndex(params.end, 'end')
597
+ }
598
+ }
599
+
600
+ const startIndex = this.normalizeSliceIndex(startValue ?? 0, totalBytes)
601
+ const endIndex = this.normalizeSliceIndex(endValue ?? totalBytes, totalBytes)
602
+
603
+ return { startIndex, endIndex }
604
+ }
605
+
606
+ private parseSliceIndex(value: any, label: string): number {
607
+ if (value === undefined || value === null) {
608
+ throw new Error(`slice-bytes: ${label} cannot be null or undefined`)
609
+ }
610
+
611
+ const normalizedValue = typeof value === 'string' ? value.trim() : value
612
+
613
+ try {
614
+ const bigIntValue = ethers.toBigInt(normalizedValue)
615
+ const maxSafe = BigInt(Number.MAX_SAFE_INTEGER)
616
+ if (bigIntValue > maxSafe || bigIntValue < -maxSafe) {
617
+ throw new Error(`${label} is outside the supported numeric range`)
618
+ }
619
+ return Number(bigIntValue)
620
+ } catch (_error) {
621
+ throw new Error(`slice-bytes: ${label} must be an integer or integer-like string`)
622
+ }
623
+ }
624
+
625
+ private normalizeSliceIndex(index: number, totalBytes: number): number {
626
+ if (!Number.isFinite(index) || !Number.isInteger(index)) {
627
+ throw new Error('slice-bytes: slice positions must be finite integers')
628
+ }
629
+
630
+ let normalized = index
631
+ if (normalized < 0) {
632
+ normalized = totalBytes + normalized
633
+ }
634
+
635
+ if (normalized < 0) {
636
+ normalized = 0
637
+ }
638
+
639
+ if (normalized > totalBytes) {
640
+ normalized = totalBytes
641
+ }
642
+
643
+ return normalized
644
+ }
645
+
533
646
  /**
534
647
  * Helper to recursively resolve the `arguments` field of any `ValueResolver` object.
535
648
  * @private
@@ -549,4 +662,4 @@ export class ValueResolver {
549
662
  }
550
663
  return this.resolve(args, context, scope)
551
664
  }
552
- }
665
+ }
@@ -315,7 +315,7 @@ actions:
315
315
 
316
316
  // --- New: Job-level constants field parsing ---
317
317
 
318
- it('should allow an optional job-level constants block as an object', () => {
318
+ it('should attach an optional job-level constants block as an object', () => {
319
319
  const yamlContent = `
320
320
  name: "job-with-constants"
321
321
  version: "1"
@@ -328,8 +328,31 @@ actions:
328
328
  arguments:
329
329
  x: "{{FEE}}"
330
330
  `
331
- const job = parseJob(yamlContent) as any
332
- // parseJob doesn't attach constants; loader attaches it. This test ensures YAML is acceptable and does not throw.
333
- expect(job.name).toBe('job-with-constants')
331
+ const job = parseJob(yamlContent)
332
+ expect(job.constants).toEqual({
333
+ FEE: '1000',
334
+ ADMIN: '0x0000000000000000000000000000000000000001'
335
+ })
336
+ })
337
+
338
+ it('should parse job-level skip conditions', () => {
339
+ const yamlContent = `
340
+ name: "job-with-skip"
341
+ version: "1"
342
+ skip_condition:
343
+ - type: "contract-exists"
344
+ arguments:
345
+ address: "0xabc"
346
+ actions:
347
+ - name: "a1"
348
+ template: "t1"
349
+ arguments: {}
350
+ `
351
+ const job = parseJob(yamlContent)
352
+ expect(job.skip_condition).toHaveLength(1)
353
+ expect(job.skip_condition?.[0]).toEqual({
354
+ type: 'contract-exists',
355
+ arguments: { address: '0xabc' }
356
+ })
334
357
  })
335
358
  })