@0xsequence/catapult 1.3.5 → 1.3.7
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 +263 -0
- package/dist/lib/__tests__/deployer.spec.js.map +1 -1
- package/dist/lib/core/__tests__/engine.spec.js +1 -1
- package/dist/lib/core/__tests__/engine.spec.js.map +1 -1
- package/dist/lib/core/engine.d.ts +2 -1
- package/dist/lib/core/engine.d.ts.map +1 -1
- package/dist/lib/core/engine.js +69 -15
- package/dist/lib/core/engine.js.map +1 -1
- package/dist/lib/core/resolver.d.ts +1 -0
- package/dist/lib/core/resolver.d.ts.map +1 -1
- package/dist/lib/core/resolver.js +18 -1
- package/dist/lib/core/resolver.js.map +1 -1
- package/dist/lib/deployer.d.ts.map +1 -1
- package/dist/lib/deployer.js +10 -4
- package/dist/lib/deployer.js.map +1 -1
- package/dist/lib/types/__tests__/resolve-json-value.spec.d.ts +2 -0
- package/dist/lib/types/__tests__/resolve-json-value.spec.d.ts.map +1 -0
- package/dist/lib/types/__tests__/resolve-json-value.spec.js +689 -0
- package/dist/lib/types/__tests__/resolve-json-value.spec.js.map +1 -0
- package/dist/lib/types/values.d.ts +5 -1
- package/dist/lib/types/values.d.ts.map +1 -1
- package/package.json +12 -13
- package/src/lib/__tests__/deployer.spec.ts +435 -93
- package/src/lib/core/__tests__/engine.spec.ts +1 -1
- package/src/lib/core/engine.ts +74 -16
- package/src/lib/core/resolver.ts +18 -1
- package/src/lib/deployer.ts +12 -2
- package/src/lib/types/__tests__/resolve-json-value.spec.ts +769 -0
- package/src/lib/types/values.ts +7 -1
|
@@ -1492,7 +1492,7 @@ describe('ExecutionEngine', () => {
|
|
|
1492
1492
|
}
|
|
1493
1493
|
|
|
1494
1494
|
await expect((engine as any).executeAction(action, context, new Map())).rejects.toThrow(new Error(`Nick's method test failed for action "nick-test-fail-twice"`))
|
|
1495
|
-
await expect((engine as any).executeAction(action, context, new Map())).rejects.toThrow(new Error('Nick\'s method test already
|
|
1495
|
+
await expect((engine as any).executeAction(action, context, new Map())).rejects.toThrow(new Error('Nick\'s method test already failed this run'))
|
|
1496
1496
|
})
|
|
1497
1497
|
|
|
1498
1498
|
it('should prevent passing Nick\'s method from being tested twice', async () => {
|
package/src/lib/core/engine.ts
CHANGED
|
@@ -29,7 +29,7 @@ export class ExecutionEngine {
|
|
|
29
29
|
private readonly noPostCheckConditions: boolean
|
|
30
30
|
private readonly allowMultipleNicksMethodTests: boolean
|
|
31
31
|
private readonly ignoreVerifyErrors: boolean
|
|
32
|
-
private
|
|
32
|
+
private nicksMethodResult: boolean | undefined
|
|
33
33
|
private verificationWarnings: Array<{
|
|
34
34
|
actionName: string
|
|
35
35
|
address: string
|
|
@@ -464,17 +464,17 @@ export class ExecutionEngine {
|
|
|
464
464
|
|
|
465
465
|
// Handle gas limit with optional multiplier
|
|
466
466
|
const network = context.getNetwork()
|
|
467
|
+
const signer = await context.getResolvedSigner()
|
|
467
468
|
if (network.gasLimit) {
|
|
468
469
|
const baseGasLimit = network.gasLimit
|
|
469
470
|
txParams.gasLimit = gasMultiplier ? Math.floor(baseGasLimit * gasMultiplier) : baseGasLimit
|
|
470
471
|
} else if (gasMultiplier) {
|
|
471
472
|
// If gasMultiplier is specified but no network gasLimit, estimate gas first
|
|
472
|
-
const signer = await context.getResolvedSigner()
|
|
473
473
|
const estimatedGas = await signer.estimateGas({ to, data, value })
|
|
474
474
|
txParams.gasLimit = Math.floor(Number(estimatedGas) * gasMultiplier)
|
|
475
475
|
}
|
|
476
476
|
|
|
477
|
-
|
|
477
|
+
await this.checkFundsForTransaction(actionName, txParams, context, signer)
|
|
478
478
|
const tx = await signer.sendTransaction(txParams)
|
|
479
479
|
|
|
480
480
|
this.events.emitEvent({
|
|
@@ -795,17 +795,17 @@ export class ExecutionEngine {
|
|
|
795
795
|
|
|
796
796
|
// Handle gas limit with optional multiplier
|
|
797
797
|
const network = context.getNetwork()
|
|
798
|
+
const signer = await context.getResolvedSigner()
|
|
798
799
|
if (network.gasLimit) {
|
|
799
800
|
const baseGasLimit = network.gasLimit
|
|
800
801
|
txParams.gasLimit = gasMultiplier ? Math.floor(baseGasLimit * gasMultiplier) : baseGasLimit
|
|
801
802
|
} else if (gasMultiplier) {
|
|
802
803
|
// If gasMultiplier is specified but no network gasLimit, estimate gas first
|
|
803
|
-
const
|
|
804
|
-
const estimatedGas = await signer.estimateGas({ to: null, data, value })
|
|
804
|
+
const estimatedGas = await signer.estimateGas(txParams)
|
|
805
805
|
txParams.gasLimit = Math.floor(Number(estimatedGas) * gasMultiplier)
|
|
806
806
|
}
|
|
807
|
-
|
|
808
|
-
|
|
807
|
+
|
|
808
|
+
await this.checkFundsForTransaction(actionName, txParams, context, signer)
|
|
809
809
|
const tx = await signer.sendTransaction(txParams)
|
|
810
810
|
|
|
811
811
|
this.events.emitEvent({
|
|
@@ -855,17 +855,19 @@ export class ExecutionEngine {
|
|
|
855
855
|
break
|
|
856
856
|
}
|
|
857
857
|
case 'test-nicks-method': {
|
|
858
|
-
if (this.
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
// Return previous result
|
|
862
|
-
break
|
|
863
|
-
}
|
|
864
|
-
} catch (e) {
|
|
865
|
-
throw new Error(`Nick's method test already performed this run`)
|
|
858
|
+
if (this.nicksMethodResult !== undefined && !this.allowMultipleNicksMethodTests) {
|
|
859
|
+
if (this.nicksMethodResult === false) {
|
|
860
|
+
throw new Error(`Nick's method test already failed this run`)
|
|
866
861
|
}
|
|
862
|
+
this.events.emitEvent({
|
|
863
|
+
type: 'debug_info',
|
|
864
|
+
level: 'debug',
|
|
865
|
+
data: {
|
|
866
|
+
message: `Nick's method test already passed this run`,
|
|
867
|
+
},
|
|
868
|
+
})
|
|
869
|
+
break
|
|
867
870
|
}
|
|
868
|
-
this.nicksMethodTested = true
|
|
869
871
|
|
|
870
872
|
// Default bytecode if none provided
|
|
871
873
|
const defaultBytecode = '0x608060405234801561001057600080fd5b5061013d806100206000396000f3fe60806040526004361061001e5760003560e01c80639c4ae2d014610023575b600080fd5b6100cb6004803603604081101561003957600080fd5b81019060208101813564010000000081111561005457600080fd5b82018360208201111561006657600080fd5b8035906020019184600183028401116401000000008311171561008857600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955050913592506100cd915050565b005b60008183516020850134f56040805173ffffffffffffffffffffffffffffffffffffffff83168152905191925081900360200190a050505056fea264697066735822122033609f614f03931b92d88c309d698449bb77efcd517328d341fa4f923c5d8c7964736f6c63430007060033'
|
|
@@ -884,6 +886,7 @@ export class ExecutionEngine {
|
|
|
884
886
|
const fundingAmount = resolvedFundingAmount ? validateBigNumberish(resolvedFundingAmount, actionName, 'fundingAmount') : undefined
|
|
885
887
|
|
|
886
888
|
const success = await this.testNicksMethod(bytecode, context, gasPrice, gasLimit, fundingAmount)
|
|
889
|
+
this.nicksMethodResult = success
|
|
887
890
|
|
|
888
891
|
if (!success) {
|
|
889
892
|
throw new Error(`Nick's method test failed for action "${actionName}"`)
|
|
@@ -1700,6 +1703,61 @@ export class ExecutionEngine {
|
|
|
1700
1703
|
return sorted
|
|
1701
1704
|
}
|
|
1702
1705
|
|
|
1706
|
+
/**
|
|
1707
|
+
* Checks if the signer has enough funds to cover the estimated cost of the transaction.
|
|
1708
|
+
* Returns true if the signer has enough funds, false if the signer does not have enough funds, and null if no gas price is available.
|
|
1709
|
+
*/
|
|
1710
|
+
private async checkFundsForTransaction(actionName: string, txParams: ethers.TransactionRequest, context: ExecutionContext, signer: ethers.Signer): Promise<boolean | null> {
|
|
1711
|
+
try {
|
|
1712
|
+
const gasPrice = txParams.gasPrice || await context.provider.getFeeData().then(data => data.gasPrice)
|
|
1713
|
+
if (!gasPrice) {
|
|
1714
|
+
this.events.emitEvent({
|
|
1715
|
+
type: 'debug_info',
|
|
1716
|
+
level: 'warn',
|
|
1717
|
+
data: {
|
|
1718
|
+
actionName: actionName,
|
|
1719
|
+
message: `No gas price available`
|
|
1720
|
+
}
|
|
1721
|
+
})
|
|
1722
|
+
return null
|
|
1723
|
+
}
|
|
1724
|
+
const gasLimit = txParams.gasLimit || await signer.estimateGas(txParams)
|
|
1725
|
+
const requiredETH = BigInt(gasLimit) * BigInt(gasPrice)
|
|
1726
|
+
const signerBalance = await context.provider.getBalance(await signer.getAddress())
|
|
1727
|
+
this.events.emitEvent({
|
|
1728
|
+
type: 'debug_info',
|
|
1729
|
+
level: 'debug',
|
|
1730
|
+
data: {
|
|
1731
|
+
actionName: actionName,
|
|
1732
|
+
message: `Transaction ${txParams.gasLimit ? 'set' : 'estimated'} gas limit: ${gasLimit}, ${txParams.gasPrice ? 'set' : 'estimated'} gas price: ${ethers.formatUnits(gasPrice, 'gwei')} gwei, required ETH: ${ethers.formatEther(requiredETH)}`
|
|
1733
|
+
}
|
|
1734
|
+
})
|
|
1735
|
+
if (signerBalance < requiredETH) {
|
|
1736
|
+
this.events.emitEvent({
|
|
1737
|
+
type: 'debug_info',
|
|
1738
|
+
level: 'warn',
|
|
1739
|
+
data: {
|
|
1740
|
+
actionName: actionName,
|
|
1741
|
+
message: `Insufficient funds: signer has ${ethers.formatEther(signerBalance)} ETH but estimated cost is ${ethers.formatEther(requiredETH)} ETH`
|
|
1742
|
+
}
|
|
1743
|
+
})
|
|
1744
|
+
return false
|
|
1745
|
+
} else {
|
|
1746
|
+
return true
|
|
1747
|
+
}
|
|
1748
|
+
} catch (error) {
|
|
1749
|
+
this.events.emitEvent({
|
|
1750
|
+
type: 'debug_info',
|
|
1751
|
+
level: 'warn',
|
|
1752
|
+
data: {
|
|
1753
|
+
actionName: actionName,
|
|
1754
|
+
message: "Error checking signer balance: " + (error instanceof Error ? error.message : String(error))
|
|
1755
|
+
}
|
|
1756
|
+
})
|
|
1757
|
+
}
|
|
1758
|
+
return null
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1703
1761
|
/**
|
|
1704
1762
|
* Get all verification warnings that were collected when ignoreVerifyErrors is enabled
|
|
1705
1763
|
*/
|
package/src/lib/core/resolver.ts
CHANGED
|
@@ -117,7 +117,7 @@ export class ValueResolver {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
// Check for Network(...) property access
|
|
120
|
-
const networkMatch = expression.match(/^Network\(\)\.(
|
|
120
|
+
const networkMatch = expression.match(/^Network\(\)\.(\w+)$/)
|
|
121
121
|
if (networkMatch) {
|
|
122
122
|
const [, property] = networkMatch
|
|
123
123
|
const network = context.getNetwork()
|
|
@@ -187,6 +187,8 @@ export class ValueResolver {
|
|
|
187
187
|
return this.resolveJobCompleted(resolvedArgs as JobCompletedValue['arguments'], context)
|
|
188
188
|
case 'read-json':
|
|
189
189
|
return this.resolveReadJson(resolvedArgs as ReadJsonValue['arguments'])
|
|
190
|
+
case 'resolve-json':
|
|
191
|
+
return this.resolveJsonValue(resolvedArgs, context)
|
|
190
192
|
default:
|
|
191
193
|
throw new Error(`Unknown value resolver type: ${(obj as any).type}`)
|
|
192
194
|
}
|
|
@@ -513,6 +515,21 @@ export class ValueResolver {
|
|
|
513
515
|
}
|
|
514
516
|
}
|
|
515
517
|
|
|
518
|
+
private async resolveJsonValue(args: any, context: ExecutionContext): Promise<any> {
|
|
519
|
+
if (Array.isArray(args)) {
|
|
520
|
+
return Promise.all(args.map(v => this.resolveJsonValue(v, context)))
|
|
521
|
+
} else if (typeof args === 'object' && args !== null) {
|
|
522
|
+
const resolved: Record<string, any> = {}
|
|
523
|
+
for (const [k, v] of Object.entries(args)) {
|
|
524
|
+
resolved[k] = await this.resolveJsonValue(v, context)
|
|
525
|
+
}
|
|
526
|
+
return resolved
|
|
527
|
+
} else {
|
|
528
|
+
// For primitive values, resolve them using the main resolve method
|
|
529
|
+
return this.resolve(args, context)
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
516
533
|
/**
|
|
517
534
|
* Helper to recursively resolve the `arguments` field of any `ValueResolver` object.
|
|
518
535
|
* @private
|
package/src/lib/deployer.ts
CHANGED
|
@@ -688,16 +688,26 @@ export class Deployer {
|
|
|
688
688
|
|
|
689
689
|
/**
|
|
690
690
|
* Populates the execution context with outputs from previously executed dependent jobs.
|
|
691
|
+
* Throws an error if any dependency failed.
|
|
691
692
|
*/
|
|
692
693
|
private populateContextWithDependentJobOutputs(job: Job, context: ExecutionContext, network: Network): void {
|
|
693
694
|
if (!job.depends_on) return
|
|
694
695
|
|
|
695
696
|
for (const dependentJobName of job.depends_on) {
|
|
696
697
|
const dependentJobResults = this.results.get(dependentJobName)
|
|
697
|
-
if (!dependentJobResults)
|
|
698
|
+
if (!dependentJobResults) {
|
|
699
|
+
throw new Error(`Job "${job.name}" depends on "${dependentJobName}", but "${dependentJobName}" has not been executed yet.`)
|
|
700
|
+
}
|
|
698
701
|
|
|
699
702
|
const networkResult = dependentJobResults.outputs.get(network.chainId)
|
|
700
|
-
if (!networkResult
|
|
703
|
+
if (!networkResult) {
|
|
704
|
+
throw new Error(`Job "${job.name}" depends on "${dependentJobName}", but "${dependentJobName}" has not been executed on network ${network.name} (chainId: ${network.chainId}).`)
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (networkResult.status !== 'success') {
|
|
708
|
+
const errorMessage = typeof networkResult.data === 'string' ? networkResult.data : 'Unknown error'
|
|
709
|
+
throw new Error(`Job "${job.name}" depends on "${dependentJobName}", but "${dependentJobName}" failed: ${errorMessage}`)
|
|
710
|
+
}
|
|
701
711
|
|
|
702
712
|
// Add outputs with job name prefixes for cross-job access
|
|
703
713
|
const outputs = networkResult.data as Map<string, unknown>
|