@0xsequence/catapult 1.3.2 → 1.3.4
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/CONCEPT.md +24 -0
- package/README.md +16 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +4 -2
- package/dist/commands/run.js.map +1 -1
- package/dist/lib/__tests__/deployer.spec.js +71 -1
- package/dist/lib/__tests__/deployer.spec.js.map +1 -1
- package/dist/lib/core/__tests__/engine.spec.js +270 -0
- package/dist/lib/core/__tests__/engine.spec.js.map +1 -1
- package/dist/lib/core/__tests__/resolver.spec.js +132 -0
- package/dist/lib/core/__tests__/resolver.spec.js.map +1 -1
- package/dist/lib/core/engine.d.ts +13 -0
- package/dist/lib/core/engine.d.ts.map +1 -1
- package/dist/lib/core/engine.js +148 -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 +24 -7
- package/dist/lib/core/resolver.js.map +1 -1
- package/dist/lib/deployer.d.ts +2 -0
- package/dist/lib/deployer.d.ts.map +1 -1
- package/dist/lib/deployer.js +18 -1
- package/dist/lib/deployer.js.map +1 -1
- package/dist/lib/events/__tests__/event-system.spec.js +30 -0
- package/dist/lib/events/__tests__/event-system.spec.js.map +1 -1
- package/dist/lib/events/cli-adapter.d.ts.map +1 -1
- package/dist/lib/events/cli-adapter.js +25 -1
- package/dist/lib/events/cli-adapter.js.map +1 -1
- package/dist/lib/events/types.d.ts +26 -2
- package/dist/lib/events/types.d.ts.map +1 -1
- package/dist/lib/std/templates/assured-deployment.yaml +4 -3
- package/dist/lib/std/templates/era-evm-predeploy.yaml +35 -0
- package/dist/lib/std/templates/erc-2470.yaml +3 -0
- package/dist/lib/std/templates/min-balance.yaml +3 -0
- package/dist/lib/std/templates/nano-universal-deployer.yaml +2 -0
- package/dist/lib/std/templates/raw-erc-2470.yaml +3 -0
- package/dist/lib/std/templates/raw-nano-universal-deployer.yaml +3 -0
- package/dist/lib/std/templates/raw-sequence-universal-deployer-2.yaml +4 -0
- package/dist/lib/std/templates/sequence-universal-deployer-2.yaml +4 -0
- package/dist/lib/types/values.d.ts +8 -1
- package/dist/lib/types/values.d.ts.map +1 -1
- package/dist/lib/utils/assertion.d.ts +4 -0
- package/dist/lib/utils/assertion.d.ts.map +1 -0
- package/dist/lib/utils/assertion.js +27 -0
- package/dist/lib/utils/assertion.js.map +1 -0
- package/package.json +12 -13
- package/src/commands/run.ts +4 -1
- package/src/lib/__tests__/deployer.spec.ts +108 -1
- package/src/lib/core/__tests__/engine.spec.ts +321 -0
- package/src/lib/core/__tests__/resolver.spec.ts +150 -1
- package/src/lib/core/engine.ts +188 -17
- package/src/lib/core/resolver.ts +29 -8
- package/src/lib/deployer.ts +28 -1
- package/src/lib/events/__tests__/event-system.spec.ts +51 -2
- package/src/lib/events/cli-adapter.ts +32 -4
- package/src/lib/events/types.ts +30 -2
- package/src/lib/std/templates/assured-deployment.yaml +4 -3
- package/src/lib/std/templates/era-evm-predeploy.yaml +35 -0
- package/src/lib/std/templates/erc-2470.yaml +3 -0
- package/src/lib/std/templates/min-balance.yaml +3 -0
- package/src/lib/std/templates/nano-universal-deployer.yaml +2 -0
- package/src/lib/std/templates/raw-erc-2470.yaml +3 -0
- package/src/lib/std/templates/raw-nano-universal-deployer.yaml +3 -0
- package/src/lib/std/templates/raw-sequence-universal-deployer-2.yaml +4 -0
- package/src/lib/std/templates/sequence-universal-deployer-2.yaml +4 -0
- package/src/lib/types/values.ts +10 -1
- package/src/lib/utils/assertion.ts +24 -0
package/src/lib/core/engine.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type EngineOptions = {
|
|
|
13
13
|
verificationRegistry?: VerificationPlatformRegistry
|
|
14
14
|
noPostCheckConditions?: boolean
|
|
15
15
|
allowMultipleNicksMethodTests?: boolean
|
|
16
|
+
ignoreVerifyErrors?: boolean
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -27,7 +28,17 @@ export class ExecutionEngine {
|
|
|
27
28
|
private readonly verificationRegistry: VerificationPlatformRegistry
|
|
28
29
|
private readonly noPostCheckConditions: boolean
|
|
29
30
|
private readonly allowMultipleNicksMethodTests: boolean
|
|
31
|
+
private readonly ignoreVerifyErrors: boolean
|
|
30
32
|
private nicksMethodTested: boolean = false
|
|
33
|
+
private verificationWarnings: Array<{
|
|
34
|
+
actionName: string
|
|
35
|
+
address: string
|
|
36
|
+
contractName: string
|
|
37
|
+
platform: string
|
|
38
|
+
error: string
|
|
39
|
+
jobName?: string
|
|
40
|
+
networkName?: string
|
|
41
|
+
}> = []
|
|
31
42
|
|
|
32
43
|
constructor(templates: Map<string, Template>, options?: EngineOptions) {
|
|
33
44
|
this.resolver = new ValueResolver()
|
|
@@ -36,6 +47,7 @@ export class ExecutionEngine {
|
|
|
36
47
|
this.verificationRegistry = options?.verificationRegistry || createDefaultVerificationRegistry()
|
|
37
48
|
this.noPostCheckConditions = options?.noPostCheckConditions ?? false
|
|
38
49
|
this.allowMultipleNicksMethodTests = options?.allowMultipleNicksMethodTests ?? false
|
|
50
|
+
this.ignoreVerifyErrors = options?.ignoreVerifyErrors ?? false
|
|
39
51
|
}
|
|
40
52
|
|
|
41
53
|
/**
|
|
@@ -631,6 +643,20 @@ export class ExecutionEngine {
|
|
|
631
643
|
)
|
|
632
644
|
anySuccess = true
|
|
633
645
|
} catch (error) {
|
|
646
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
647
|
+
|
|
648
|
+
// If ignoreVerifyErrors is enabled, add to warnings and continue
|
|
649
|
+
if (this.ignoreVerifyErrors) {
|
|
650
|
+
this.verificationWarnings.push({
|
|
651
|
+
actionName: actionName,
|
|
652
|
+
address,
|
|
653
|
+
contractName,
|
|
654
|
+
platform: platform.name,
|
|
655
|
+
error: errorMessage,
|
|
656
|
+
networkName: network.name
|
|
657
|
+
})
|
|
658
|
+
}
|
|
659
|
+
|
|
634
660
|
// Log the error but continue with other platforms
|
|
635
661
|
this.events.emitEvent({
|
|
636
662
|
type: 'verification_failed',
|
|
@@ -640,14 +666,26 @@ export class ExecutionEngine {
|
|
|
640
666
|
address,
|
|
641
667
|
contractName,
|
|
642
668
|
platform: platform.name,
|
|
643
|
-
error:
|
|
669
|
+
error: errorMessage
|
|
644
670
|
}
|
|
645
671
|
})
|
|
646
672
|
}
|
|
647
673
|
}
|
|
648
674
|
|
|
649
675
|
if (!anySuccess) {
|
|
650
|
-
|
|
676
|
+
if (this.ignoreVerifyErrors) {
|
|
677
|
+
// Don't throw error if ignoreVerifyErrors is enabled - warnings already collected
|
|
678
|
+
this.events.emitEvent({
|
|
679
|
+
type: 'verification_skipped',
|
|
680
|
+
level: 'warn',
|
|
681
|
+
data: {
|
|
682
|
+
actionName: actionName,
|
|
683
|
+
reason: `Verification failed on all configured platforms for network ${network.name}, but continuing due to --ignore-verify-errors`
|
|
684
|
+
}
|
|
685
|
+
})
|
|
686
|
+
} else {
|
|
687
|
+
throw new Error(`Verification failed on all configured platforms for network ${network.name}`)
|
|
688
|
+
}
|
|
651
689
|
}
|
|
652
690
|
} else {
|
|
653
691
|
// Handle specific platform(s) verification
|
|
@@ -673,6 +711,20 @@ export class ExecutionEngine {
|
|
|
673
711
|
)
|
|
674
712
|
anySuccess = true
|
|
675
713
|
} catch (error) {
|
|
714
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
715
|
+
|
|
716
|
+
// If ignoreVerifyErrors is enabled, add to warnings
|
|
717
|
+
if (this.ignoreVerifyErrors) {
|
|
718
|
+
this.verificationWarnings.push({
|
|
719
|
+
actionName: actionName,
|
|
720
|
+
address,
|
|
721
|
+
contractName,
|
|
722
|
+
platform: platform.name,
|
|
723
|
+
error: errorMessage,
|
|
724
|
+
networkName: network.name
|
|
725
|
+
})
|
|
726
|
+
}
|
|
727
|
+
|
|
676
728
|
// Log the error but continue with other platforms if multiple specified
|
|
677
729
|
this.events.emitEvent({
|
|
678
730
|
type: 'verification_failed',
|
|
@@ -682,19 +734,31 @@ export class ExecutionEngine {
|
|
|
682
734
|
address,
|
|
683
735
|
contractName,
|
|
684
736
|
platform: platform.name,
|
|
685
|
-
error:
|
|
737
|
+
error: errorMessage
|
|
686
738
|
}
|
|
687
739
|
})
|
|
688
740
|
|
|
689
|
-
// If only one platform specified, re-throw the error
|
|
690
|
-
if (platformsToTry.length === 1) {
|
|
741
|
+
// If only one platform specified, re-throw the error unless ignoreVerifyErrors is enabled
|
|
742
|
+
if (platformsToTry.length === 1 && !this.ignoreVerifyErrors) {
|
|
691
743
|
throw error
|
|
692
744
|
}
|
|
693
745
|
}
|
|
694
746
|
}
|
|
695
747
|
|
|
696
748
|
if (!anySuccess && platformsToTry.length > 1) {
|
|
697
|
-
|
|
749
|
+
if (this.ignoreVerifyErrors) {
|
|
750
|
+
// Don't throw error if ignoreVerifyErrors is enabled - warnings already collected
|
|
751
|
+
this.events.emitEvent({
|
|
752
|
+
type: 'verification_skipped',
|
|
753
|
+
level: 'warn',
|
|
754
|
+
data: {
|
|
755
|
+
actionName: actionName,
|
|
756
|
+
reason: `Verification failed on all specified platforms: ${platformsToTry.join(', ')}, but continuing due to --ignore-verify-errors`
|
|
757
|
+
}
|
|
758
|
+
})
|
|
759
|
+
} else {
|
|
760
|
+
throw new Error(`Verification failed on all specified platforms: ${platformsToTry.join(', ')}`)
|
|
761
|
+
}
|
|
698
762
|
}
|
|
699
763
|
}
|
|
700
764
|
|
|
@@ -1126,9 +1190,76 @@ export class ExecutionEngine {
|
|
|
1126
1190
|
|
|
1127
1191
|
// Generate a valid ECDSA signature using Nick's method approach
|
|
1128
1192
|
const result = await this.generateNicksMethodTransaction(bytecode, defaultGasPrice, defaultGasLimit)
|
|
1129
|
-
const
|
|
1193
|
+
const {signedTx, unsignedTx} = result
|
|
1130
1194
|
eoaAddress = result.eoaAddress
|
|
1131
1195
|
wallet = result.wallet
|
|
1196
|
+
|
|
1197
|
+
// Simulate the contract creation transaction
|
|
1198
|
+
try {
|
|
1199
|
+
const simulationTx = {
|
|
1200
|
+
...unsignedTx,
|
|
1201
|
+
from: eoaAddress,
|
|
1202
|
+
};
|
|
1203
|
+
|
|
1204
|
+
if (unsignedTx.gasPrice) {
|
|
1205
|
+
// Check gas price
|
|
1206
|
+
const gasPrice = await context.provider.getFeeData().then(data => data.gasPrice)
|
|
1207
|
+
if (!gasPrice) {
|
|
1208
|
+
this.events.emitEvent({
|
|
1209
|
+
type: "debug_info",
|
|
1210
|
+
level: "debug",
|
|
1211
|
+
data: {
|
|
1212
|
+
message: `Legacy gas price not available.`,
|
|
1213
|
+
},
|
|
1214
|
+
});
|
|
1215
|
+
} else if (BigInt(unsignedTx.gasPrice.toString()) < gasPrice) {
|
|
1216
|
+
this.events.emitEvent({
|
|
1217
|
+
type: "debug_info",
|
|
1218
|
+
level: "warn",
|
|
1219
|
+
data: {
|
|
1220
|
+
message: `Gas price (${unsignedTx.gasPrice}) is lower than the current gas price (${gasPrice}). This may cause the transaction to not be mined.`,
|
|
1221
|
+
},
|
|
1222
|
+
});
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
if (simulationTx.gasLimit) {
|
|
1227
|
+
// Simulate the transaction expected gas usage
|
|
1228
|
+
const estimatedGas = await context.provider.estimateGas(simulationTx);
|
|
1229
|
+
const estimatedGasStr = estimatedGas.toString();
|
|
1230
|
+
const simulationTxGasLimitStr = simulationTx.gasLimit.toString();
|
|
1231
|
+
if (estimatedGas > BigInt(simulationTxGasLimitStr)) {
|
|
1232
|
+
this.events.emitEvent({
|
|
1233
|
+
type: "debug_info",
|
|
1234
|
+
level: "warn",
|
|
1235
|
+
data: {
|
|
1236
|
+
message: `Estimated gas (${estimatedGasStr}) is greater than gas provided in the transaction (${simulationTxGasLimitStr}). This may cause the transaction to revert.`,
|
|
1237
|
+
},
|
|
1238
|
+
});
|
|
1239
|
+
} else {
|
|
1240
|
+
this.events.emitEvent({
|
|
1241
|
+
type: "debug_info",
|
|
1242
|
+
level: "debug",
|
|
1243
|
+
data: {
|
|
1244
|
+
message: `Estimated gas: ${estimatedGasStr}, Gas provided: ${simulationTxGasLimitStr}`,
|
|
1245
|
+
},
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
} catch (simulationError) {
|
|
1250
|
+
this.events.emitEvent({
|
|
1251
|
+
type: "debug_info",
|
|
1252
|
+
level: "warn",
|
|
1253
|
+
data: {
|
|
1254
|
+
message: `Simulation failed: ${
|
|
1255
|
+
simulationError instanceof Error
|
|
1256
|
+
? simulationError.message
|
|
1257
|
+
: String(simulationError)
|
|
1258
|
+
}`,
|
|
1259
|
+
},
|
|
1260
|
+
});
|
|
1261
|
+
// Continue with the test even if simulation fails
|
|
1262
|
+
}
|
|
1132
1263
|
|
|
1133
1264
|
this.events.emitEvent({
|
|
1134
1265
|
type: 'debug_info',
|
|
@@ -1221,11 +1352,11 @@ export class ExecutionEngine {
|
|
|
1221
1352
|
type: 'debug_info',
|
|
1222
1353
|
level: 'debug',
|
|
1223
1354
|
data: {
|
|
1224
|
-
message: `[NICK'S METHOD DEBUG] Broadcasting Nick's method transaction. RawTx: ${
|
|
1355
|
+
message: `[NICK'S METHOD DEBUG] Broadcasting Nick's method transaction. RawTx: ${signedTx.substring(0, 100)}...`
|
|
1225
1356
|
}
|
|
1226
1357
|
})
|
|
1227
1358
|
|
|
1228
|
-
const deployTx = await context.provider.broadcastTransaction(
|
|
1359
|
+
const deployTx = await context.provider.broadcastTransaction(signedTx)
|
|
1229
1360
|
|
|
1230
1361
|
this.events.emitEvent({
|
|
1231
1362
|
type: 'debug_info',
|
|
@@ -1322,7 +1453,7 @@ export class ExecutionEngine {
|
|
|
1322
1453
|
bytecode: string,
|
|
1323
1454
|
gasPrice: ethers.BigNumberish,
|
|
1324
1455
|
gasLimit: ethers.BigNumberish
|
|
1325
|
-
): Promise<{
|
|
1456
|
+
): Promise<{ unsignedTx: ethers.TransactionRequest; signedTx: string; eoaAddress: string; wallet: ethers.HDNodeWallet }> {
|
|
1326
1457
|
// Generate a random private key for the test
|
|
1327
1458
|
const wallet = ethers.Wallet.createRandom()
|
|
1328
1459
|
|
|
@@ -1346,9 +1477,10 @@ export class ExecutionEngine {
|
|
|
1346
1477
|
const eoaAddress = parsedTx.from!
|
|
1347
1478
|
|
|
1348
1479
|
return {
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1480
|
+
unsignedTx,
|
|
1481
|
+
signedTx,
|
|
1482
|
+
eoaAddress,
|
|
1483
|
+
wallet,
|
|
1352
1484
|
}
|
|
1353
1485
|
}
|
|
1354
1486
|
|
|
@@ -1372,9 +1504,26 @@ export class ExecutionEngine {
|
|
|
1372
1504
|
const connectedWallet = wallet.connect(context.provider)
|
|
1373
1505
|
|
|
1374
1506
|
// Estimate gas for a simple transfer
|
|
1375
|
-
const
|
|
1507
|
+
const feeData = await context.provider.getFeeData()
|
|
1508
|
+
const txGas = feeData.maxFeePerGas ? {
|
|
1509
|
+
maxFeePerGas: feeData.maxFeePerGas,
|
|
1510
|
+
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas || ethers.parseUnits('20', 'gwei')
|
|
1511
|
+
} : {
|
|
1512
|
+
gasPrice: feeData.gasPrice || undefined,
|
|
1513
|
+
}
|
|
1514
|
+
const effectiveGasPrice = txGas.maxFeePerGas || txGas.gasPrice
|
|
1515
|
+
if (!effectiveGasPrice) {
|
|
1516
|
+
this.events.emitEvent({
|
|
1517
|
+
type: 'action_failed',
|
|
1518
|
+
level: 'error',
|
|
1519
|
+
data: {
|
|
1520
|
+
message: `No gas price available`
|
|
1521
|
+
}
|
|
1522
|
+
})
|
|
1523
|
+
return
|
|
1524
|
+
}
|
|
1376
1525
|
const gasLimit = 21000n // Standard gas limit for ETH transfer
|
|
1377
|
-
const gasCost =
|
|
1526
|
+
const gasCost = effectiveGasPrice * gasLimit
|
|
1378
1527
|
|
|
1379
1528
|
// Check if we have enough balance to cover gas costs
|
|
1380
1529
|
if (remainingBalance <= gasCost) {
|
|
@@ -1406,8 +1555,8 @@ export class ExecutionEngine {
|
|
|
1406
1555
|
const returnTx = await connectedWallet.sendTransaction({
|
|
1407
1556
|
to: await (await context.getResolvedSigner()).getAddress(),
|
|
1408
1557
|
value: amountToSend,
|
|
1409
|
-
|
|
1410
|
-
|
|
1558
|
+
gasLimit: gasLimit,
|
|
1559
|
+
...txGas,
|
|
1411
1560
|
})
|
|
1412
1561
|
|
|
1413
1562
|
await returnTx.wait()
|
|
@@ -1550,4 +1699,26 @@ export class ExecutionEngine {
|
|
|
1550
1699
|
|
|
1551
1700
|
return sorted
|
|
1552
1701
|
}
|
|
1702
|
+
|
|
1703
|
+
/**
|
|
1704
|
+
* Get all verification warnings that were collected when ignoreVerifyErrors is enabled
|
|
1705
|
+
*/
|
|
1706
|
+
public getVerificationWarnings(): Array<{
|
|
1707
|
+
actionName: string
|
|
1708
|
+
address: string
|
|
1709
|
+
contractName: string
|
|
1710
|
+
platform: string
|
|
1711
|
+
error: string
|
|
1712
|
+
jobName?: string
|
|
1713
|
+
networkName?: string
|
|
1714
|
+
}> {
|
|
1715
|
+
return [...this.verificationWarnings]
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
/**
|
|
1719
|
+
* Clear verification warnings (useful for testing)
|
|
1720
|
+
*/
|
|
1721
|
+
public clearVerificationWarnings(): void {
|
|
1722
|
+
this.verificationWarnings = []
|
|
1723
|
+
}
|
|
1553
1724
|
}
|
package/src/lib/core/resolver.ts
CHANGED
|
@@ -5,16 +5,17 @@ import {
|
|
|
5
5
|
AbiEncodeValue,
|
|
6
6
|
AbiPackValue,
|
|
7
7
|
ConstructorEncodeValue,
|
|
8
|
+
ComputeCreateValue,
|
|
8
9
|
ComputeCreate2Value,
|
|
9
10
|
ReadBalanceValue,
|
|
10
11
|
BasicArithmeticValue,
|
|
11
12
|
CallValue,
|
|
12
|
-
ContractExistsCondition,
|
|
13
13
|
ContractExistsValue,
|
|
14
14
|
JobCompletedValue,
|
|
15
15
|
ReadJsonValue,
|
|
16
16
|
} from '../types'
|
|
17
17
|
import { ExecutionContext } from './context'
|
|
18
|
+
import { isAddress, isBigNumberish, isBytesLike } from '../utils/assertion'
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* A scope for resolving local variables, such as template arguments.
|
|
@@ -154,6 +155,8 @@ export class ValueResolver {
|
|
|
154
155
|
return this.resolveAbiPack(resolvedArgs as AbiPackValue['arguments'])
|
|
155
156
|
case 'constructor-encode':
|
|
156
157
|
return this.resolveConstructorEncode(resolvedArgs as ConstructorEncodeValue['arguments'])
|
|
158
|
+
case 'compute-create':
|
|
159
|
+
return this.resolveComputeCreate(resolvedArgs as ComputeCreateValue['arguments'])
|
|
157
160
|
case 'compute-create2':
|
|
158
161
|
return this.resolveComputeCreate2(resolvedArgs as ComputeCreate2Value['arguments'])
|
|
159
162
|
case 'read-balance':
|
|
@@ -260,7 +263,7 @@ export class ValueResolver {
|
|
|
260
263
|
}
|
|
261
264
|
|
|
262
265
|
// Validate that creation code is valid bytecode
|
|
263
|
-
if (!
|
|
266
|
+
if (!isBytesLike(creationCode)) {
|
|
264
267
|
throw new Error(`Invalid creation code: ${creationCode}`)
|
|
265
268
|
}
|
|
266
269
|
|
|
@@ -280,18 +283,36 @@ export class ValueResolver {
|
|
|
280
283
|
return '0x' + cleanCreationCode + cleanEncodedArgs
|
|
281
284
|
}
|
|
282
285
|
|
|
286
|
+
private resolveComputeCreate(args: ComputeCreateValue['arguments']): string {
|
|
287
|
+
const { deployerAddress, nonce } = args
|
|
288
|
+
// Check if the deployer address is a valid address
|
|
289
|
+
if (!isAddress(deployerAddress)) {
|
|
290
|
+
throw new Error(`Invalid deployer address: ${deployerAddress}`)
|
|
291
|
+
}
|
|
292
|
+
// Check if the nonce is a valid value
|
|
293
|
+
if (!isBigNumberish(nonce)) {
|
|
294
|
+
throw new Error(`Invalid nonce: ${nonce}`)
|
|
295
|
+
}
|
|
296
|
+
const bnNonce = ethers.toBigInt(nonce)
|
|
297
|
+
// Create the create address
|
|
298
|
+
return ethers.getCreateAddress({
|
|
299
|
+
from: deployerAddress,
|
|
300
|
+
nonce: bnNonce,
|
|
301
|
+
})
|
|
302
|
+
}
|
|
303
|
+
|
|
283
304
|
private resolveComputeCreate2(args: ComputeCreate2Value['arguments']): string {
|
|
284
305
|
const { deployerAddress, salt, initCode } = args
|
|
285
306
|
// Check if the deployer address is a valid address
|
|
286
|
-
if (!
|
|
307
|
+
if (!isAddress(deployerAddress)) {
|
|
287
308
|
throw new Error(`Invalid deployer address: ${deployerAddress}`)
|
|
288
309
|
}
|
|
289
310
|
// Check if the salt is a valid bytes value
|
|
290
|
-
if (!
|
|
311
|
+
if (!isBytesLike(salt)) {
|
|
291
312
|
throw new Error(`Invalid salt: ${salt}`)
|
|
292
313
|
}
|
|
293
314
|
// Check if the init code is a valid bytes value
|
|
294
|
-
if (!
|
|
315
|
+
if (!isBytesLike(initCode)) {
|
|
295
316
|
throw new Error(`Invalid init code: ${initCode}`)
|
|
296
317
|
}
|
|
297
318
|
// Hash the init code using Keccak256
|
|
@@ -304,7 +325,7 @@ export class ValueResolver {
|
|
|
304
325
|
// Check if the address is a valid address
|
|
305
326
|
const addressValue = args.address as any
|
|
306
327
|
|
|
307
|
-
if (!
|
|
328
|
+
if (!isAddress(addressValue)) {
|
|
308
329
|
throw new Error(`Invalid address: ${addressValue}`)
|
|
309
330
|
}
|
|
310
331
|
|
|
@@ -349,7 +370,7 @@ export class ValueResolver {
|
|
|
349
370
|
}
|
|
350
371
|
|
|
351
372
|
// Validate that the target address is a valid Ethereum address
|
|
352
|
-
if (!
|
|
373
|
+
if (!isAddress(to)) {
|
|
353
374
|
throw new Error(`call: invalid target address: ${to}`)
|
|
354
375
|
}
|
|
355
376
|
|
|
@@ -407,7 +428,7 @@ export class ValueResolver {
|
|
|
407
428
|
private async resolveContractExists(args: ContractExistsValue['arguments'], context: ExecutionContext): Promise<boolean> {
|
|
408
429
|
const { address } = args
|
|
409
430
|
|
|
410
|
-
if (!
|
|
431
|
+
if (!isAddress(address)) {
|
|
411
432
|
throw new Error(`contract-exists: invalid address: ${address}`)
|
|
412
433
|
}
|
|
413
434
|
|
package/src/lib/deployer.ts
CHANGED
|
@@ -52,6 +52,9 @@ export interface DeployerOptions {
|
|
|
52
52
|
|
|
53
53
|
/** Optional: Show end-of-run summary (default: true). */
|
|
54
54
|
showSummary?: boolean
|
|
55
|
+
|
|
56
|
+
/** Optional: Convert verification errors to warnings instead of failing (default: false). */
|
|
57
|
+
ignoreVerifyErrors?: boolean
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
/**
|
|
@@ -157,7 +160,8 @@ export class Deployer {
|
|
|
157
160
|
const engine = new ExecutionEngine(this.loader.templates, {
|
|
158
161
|
eventEmitter: this.events,
|
|
159
162
|
verificationRegistry,
|
|
160
|
-
noPostCheckConditions: this.noPostCheckConditions
|
|
163
|
+
noPostCheckConditions: this.noPostCheckConditions,
|
|
164
|
+
ignoreVerifyErrors: this.options.ignoreVerifyErrors ?? false
|
|
161
165
|
})
|
|
162
166
|
|
|
163
167
|
// Track if any jobs have failed
|
|
@@ -312,6 +316,11 @@ export class Deployer {
|
|
|
312
316
|
// 5. Write results to output files.
|
|
313
317
|
await this.writeOutputFiles()
|
|
314
318
|
|
|
319
|
+
// Show verification warnings report if ignoreVerifyErrors is enabled
|
|
320
|
+
if (this.options.ignoreVerifyErrors) {
|
|
321
|
+
this.emitVerificationWarningsReport(engine)
|
|
322
|
+
}
|
|
323
|
+
|
|
315
324
|
// Emit end-of-run summary before final status
|
|
316
325
|
if (this.showSummary) {
|
|
317
326
|
this.emitRunSummary(hasFailures)
|
|
@@ -423,6 +432,24 @@ export class Deployer {
|
|
|
423
432
|
this.events.emitEvent(summaryEvent)
|
|
424
433
|
}
|
|
425
434
|
|
|
435
|
+
/**
|
|
436
|
+
* Emit verification warnings report when ignoreVerifyErrors is enabled
|
|
437
|
+
*/
|
|
438
|
+
private emitVerificationWarningsReport(engine: ExecutionEngine): void {
|
|
439
|
+
const warnings = engine.getVerificationWarnings()
|
|
440
|
+
|
|
441
|
+
if (warnings.length > 0) {
|
|
442
|
+
this.events.emitEvent({
|
|
443
|
+
type: 'verification_warnings_report',
|
|
444
|
+
level: 'warn',
|
|
445
|
+
data: {
|
|
446
|
+
totalWarnings: warnings.length,
|
|
447
|
+
warnings: warnings
|
|
448
|
+
}
|
|
449
|
+
})
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
426
453
|
/**
|
|
427
454
|
* Determines the final, ordered list of jobs to execute based on user input.
|
|
428
455
|
* If a user requests specific jobs, this ensures all their dependencies are also included.
|
|
@@ -11,7 +11,7 @@ describe('Event System', () => {
|
|
|
11
11
|
beforeEach(() => {
|
|
12
12
|
eventEmitter = new DeploymentEventEmitter()
|
|
13
13
|
cliAdapter = new CLIEventAdapter(eventEmitter, 3)
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
// Mock console methods
|
|
16
16
|
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {})
|
|
17
17
|
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
|
|
@@ -339,5 +339,54 @@ describe('Event System', () => {
|
|
|
339
339
|
|
|
340
340
|
expect(handler).not.toHaveBeenCalled()
|
|
341
341
|
})
|
|
342
|
+
|
|
343
|
+
it('should respect debug_info event level for verbosity filtering', () => {
|
|
344
|
+
// Test with verbosity 0 (default) - should NOT show any debug_info events
|
|
345
|
+
cliAdapter.setVerbosity(0)
|
|
346
|
+
|
|
347
|
+
eventEmitter.emitEvent({
|
|
348
|
+
type: 'debug_info',
|
|
349
|
+
level: 'warn',
|
|
350
|
+
data: {
|
|
351
|
+
message: 'This warning should NOT be shown at verbosity 0'
|
|
352
|
+
}
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
expect(consoleLogSpy).not.toHaveBeenCalled()
|
|
356
|
+
|
|
357
|
+
// Clear previous calls
|
|
358
|
+
consoleLogSpy.mockClear()
|
|
359
|
+
|
|
360
|
+
// Test with verbosity 3 - should show warn level with correct formatting
|
|
361
|
+
cliAdapter.setVerbosity(3)
|
|
362
|
+
|
|
363
|
+
eventEmitter.emitEvent({
|
|
364
|
+
type: 'debug_info',
|
|
365
|
+
level: 'warn',
|
|
366
|
+
data: {
|
|
367
|
+
message: 'This warning should be shown at verbosity 3'
|
|
368
|
+
}
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
372
|
+
expect.stringContaining('[WARN] This warning should be shown at verbosity 3')
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
// Clear previous calls
|
|
376
|
+
consoleLogSpy.mockClear()
|
|
377
|
+
|
|
378
|
+
// Test with verbosity 3 - should show debug level with correct formatting
|
|
379
|
+
eventEmitter.emitEvent({
|
|
380
|
+
type: 'debug_info',
|
|
381
|
+
level: 'debug',
|
|
382
|
+
data: {
|
|
383
|
+
message: 'This debug message should be shown at verbosity 3'
|
|
384
|
+
}
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
388
|
+
expect.stringContaining('[DEBUG] This debug message should be shown at verbosity 3')
|
|
389
|
+
)
|
|
390
|
+
})
|
|
342
391
|
})
|
|
343
|
-
})
|
|
392
|
+
})
|
|
@@ -5,7 +5,7 @@ import { DeploymentEventEmitter } from './emitter'
|
|
|
5
5
|
/**
|
|
6
6
|
* Verbosity levels for filtering console output:
|
|
7
7
|
* 0 (default): Critical info only - errors, warnings, main deployment steps
|
|
8
|
-
* 1 (-v): Add transaction details and verification steps
|
|
8
|
+
* 1 (-v): Add transaction details and verification steps
|
|
9
9
|
* 2 (-vv): Add action details and file operations
|
|
10
10
|
* 3 (-vvv): Full debug - show everything including template transitions
|
|
11
11
|
*/
|
|
@@ -81,7 +81,7 @@ export class CLIEventAdapter {
|
|
|
81
81
|
if (level1Events.has(eventType)) return 1
|
|
82
82
|
if (level2Events.has(eventType)) return 2
|
|
83
83
|
if (level3Events.has(eventType)) return 3
|
|
84
|
-
|
|
84
|
+
|
|
85
85
|
// Default to level 3 for any new events we haven't categorized
|
|
86
86
|
return 3
|
|
87
87
|
}
|
|
@@ -266,6 +266,30 @@ export class CLIEventAdapter {
|
|
|
266
266
|
// Keep short to reduce noise
|
|
267
267
|
console.log(chalk.gray(` Verification retry ${event.data.attempt}/${event.data.maxRetries}: ${event.data.error}`))
|
|
268
268
|
break
|
|
269
|
+
|
|
270
|
+
case 'verification_skipped':
|
|
271
|
+
console.log(chalk.yellow(` ⚠️ ${event.data.reason}`))
|
|
272
|
+
break
|
|
273
|
+
|
|
274
|
+
case 'verification_warnings_report':
|
|
275
|
+
// Display detailed verification warnings report
|
|
276
|
+
console.log(chalk.yellow('\n📋 Verification Warnings Report'))
|
|
277
|
+
console.log(chalk.yellow(` Total warnings: ${event.data.totalWarnings}`))
|
|
278
|
+
console.log('')
|
|
279
|
+
|
|
280
|
+
if (event.data.warnings && event.data.warnings.length > 0) {
|
|
281
|
+
for (const warning of event.data.warnings) {
|
|
282
|
+
console.log(chalk.red(` ❌ ${warning.actionName} (${warning.contractName})`))
|
|
283
|
+
console.log(chalk.gray(` Address: ${warning.address}`))
|
|
284
|
+
console.log(chalk.gray(` Platform: ${warning.platform}`))
|
|
285
|
+
if (warning.networkName) {
|
|
286
|
+
console.log(chalk.gray(` Network: ${warning.networkName}`))
|
|
287
|
+
}
|
|
288
|
+
console.log(chalk.gray(` Error: ${warning.error}`))
|
|
289
|
+
console.log('')
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
break
|
|
269
293
|
case 'contract_created':
|
|
270
294
|
console.log(chalk.gray(` contract: ${event.data.contractAddress}`))
|
|
271
295
|
break
|
|
@@ -314,7 +338,11 @@ export class CLIEventAdapter {
|
|
|
314
338
|
break
|
|
315
339
|
|
|
316
340
|
case 'debug_info':
|
|
317
|
-
|
|
341
|
+
const levelPrefix = event.level.toUpperCase()
|
|
342
|
+
const levelColor = event.level === 'warn' ? chalk.yellow :
|
|
343
|
+
event.level === 'info' ? chalk.blue :
|
|
344
|
+
chalk.gray
|
|
345
|
+
console.log(levelColor(` [${levelPrefix}] ${event.data.message}`))
|
|
318
346
|
break
|
|
319
347
|
|
|
320
348
|
default:
|
|
@@ -338,4 +366,4 @@ export class CLIEventAdapter {
|
|
|
338
366
|
public destroy(): void {
|
|
339
367
|
this.emitter.removeAllListeners()
|
|
340
368
|
}
|
|
341
|
-
}
|
|
369
|
+
}
|
package/src/lib/events/types.ts
CHANGED
|
@@ -147,7 +147,7 @@ export interface ActionInfoEvent extends BaseEvent {
|
|
|
147
147
|
|
|
148
148
|
export interface DebugInfoEvent extends BaseEvent {
|
|
149
149
|
type: 'debug_info'
|
|
150
|
-
level: 'debug'
|
|
150
|
+
level: 'debug' | 'info' | 'warn'
|
|
151
151
|
data: {
|
|
152
152
|
message: string
|
|
153
153
|
}
|
|
@@ -429,6 +429,32 @@ export interface VerificationRetryEvent extends BaseEvent {
|
|
|
429
429
|
}
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
+
export interface VerificationSkippedEvent extends BaseEvent {
|
|
433
|
+
type: 'verification_skipped'
|
|
434
|
+
level: 'warn'
|
|
435
|
+
data: {
|
|
436
|
+
actionName: string
|
|
437
|
+
reason: string
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
export interface VerificationWarningsReportEvent extends BaseEvent {
|
|
442
|
+
type: 'verification_warnings_report'
|
|
443
|
+
level: 'warn'
|
|
444
|
+
data: {
|
|
445
|
+
totalWarnings: number
|
|
446
|
+
warnings: Array<{
|
|
447
|
+
actionName: string
|
|
448
|
+
address: string
|
|
449
|
+
contractName: string
|
|
450
|
+
platform: string
|
|
451
|
+
error: string
|
|
452
|
+
jobName?: string
|
|
453
|
+
networkName?: string
|
|
454
|
+
}>
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
432
458
|
// End-of-run summary
|
|
433
459
|
export interface RunSummaryEvent extends BaseEvent {
|
|
434
460
|
type: 'run_summary'
|
|
@@ -488,5 +514,7 @@ export type DeploymentEvent =
|
|
|
488
514
|
| VerificationSubmittedEvent
|
|
489
515
|
| VerificationCompletedEvent
|
|
490
516
|
| VerificationFailedEvent
|
|
491
|
-
| VerificationRetryEvent
|
|
517
|
+
| VerificationRetryEvent
|
|
518
|
+
| VerificationSkippedEvent
|
|
519
|
+
| VerificationWarningsReportEvent
|
|
492
520
|
| RunSummaryEvent
|
|
@@ -13,17 +13,18 @@
|
|
|
13
13
|
# code before the call data, hence the wrapping pattern.
|
|
14
14
|
name: "assured-deployment"
|
|
15
15
|
type: "template"
|
|
16
|
+
description: "Wraps a factory call with a CREATE that runs a minimal assurance program"
|
|
16
17
|
|
|
17
18
|
arguments:
|
|
18
|
-
# The address that is expected to exist after the factory call (e.g., CREATE2 result)
|
|
19
19
|
targetAddress:
|
|
20
20
|
type: "address"
|
|
21
|
-
|
|
21
|
+
description: "The address that is expected to exist after the factory call (e.g., CREATE2 result)"
|
|
22
22
|
factoryAddress:
|
|
23
23
|
type: "address"
|
|
24
|
-
|
|
24
|
+
description: "The factory that will perform the deployment"
|
|
25
25
|
callData:
|
|
26
26
|
type: "bytes"
|
|
27
|
+
description: "The encoded call to the factory (e.g., abi-encoded deploy(...))"
|
|
27
28
|
|
|
28
29
|
actions:
|
|
29
30
|
- type: "create-contract"
|