@aztec/l1-artifacts 0.0.1-commit.8227e42 → 0.0.1-commit.85d7d01
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/l1-contracts/cache/solidity-files-cache.json +1 -1
- package/l1-contracts/generated/default.json +1 -1
- package/l1-contracts/out/DeployAztecL1Contracts.s.sol/DeployAztecL1Contracts.json +1 -1
- package/l1-contracts/out/DeployAztecL1Contracts.t.sol/DeployAztecL1ContractsTest.json +1 -1
- package/l1-contracts/out/DeployRollupForUpgrade.s.sol/DeployRollupForUpgrade.json +1 -1
- package/l1-contracts/out/DeployRollupForUpgrade.t.sol/DeployRollupForUpgradeTest.json +1 -1
- package/l1-contracts/out/DeployRollupLib.sol/DeployRollupLib.json +1 -1
- package/l1-contracts/out/DeploymentConfiguration.sol/DeploymentConfiguration.json +1 -1
- package/l1-contracts/out/DeploymentConfiguration.sol/IDeploymentConfiguration.json +1 -1
- package/l1-contracts/out/RollupConfiguration.sol/IRollupConfiguration.json +1 -1
- package/l1-contracts/out/RollupConfiguration.sol/RollupConfiguration.json +1 -1
- package/l1-contracts/out/build-info/{09862803192e8bf5.json → 829c69e12cc2d6eb.json} +1 -1
- package/l1-contracts/script/deploy/DeploymentConfiguration.sol +1 -1
- package/l1-contracts/script/deploy/RollupConfiguration.sol +1 -1
- package/l1-contracts/scripts/forge_broadcast.js +49 -303
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"id":"
|
|
1
|
+
{"id":"829c69e12cc2d6eb","source_id_to_path":{"0":"generated/HonkVerifier.sol","1":"lib/circuits/src/solidity/src/Constants.sol","2":"lib/circuits/src/solidity/src/DateUtils.sol","3":"lib/circuits/src/solidity/src/IProofVerifier.sol","4":"lib/circuits/src/solidity/src/IRootRegistry.sol","5":"lib/circuits/src/solidity/src/InputsExtractor.sol","6":"lib/circuits/src/solidity/src/StringUtils.sol","7":"lib/circuits/src/solidity/src/Types.sol","8":"lib/circuits/src/solidity/src/ZKPassportHelper.sol","9":"lib/circuits/src/solidity/src/ZKPassportRootVerifier.sol","10":"lib/circuits/src/solidity/src/ZKPassportSubVerifier.sol","11":"lib/forge-std/src/Base.sol","12":"lib/forge-std/src/Script.sol","13":"lib/forge-std/src/StdAssertions.sol","14":"lib/forge-std/src/StdChains.sol","15":"lib/forge-std/src/StdCheats.sol","16":"lib/forge-std/src/StdError.sol","17":"lib/forge-std/src/StdInvariant.sol","18":"lib/forge-std/src/StdJson.sol","19":"lib/forge-std/src/StdMath.sol","20":"lib/forge-std/src/StdStorage.sol","21":"lib/forge-std/src/StdStyle.sol","22":"lib/forge-std/src/StdToml.sol","23":"lib/forge-std/src/StdUtils.sol","24":"lib/forge-std/src/Test.sol","25":"lib/forge-std/src/Vm.sol","26":"lib/forge-std/src/console.sol","27":"lib/forge-std/src/console2.sol","28":"lib/forge-std/src/interfaces/IERC165.sol","29":"lib/forge-std/src/interfaces/IERC20.sol","30":"lib/forge-std/src/interfaces/IERC721.sol","31":"lib/forge-std/src/interfaces/IMulticall3.sol","32":"lib/forge-std/src/mocks/MockERC20.sol","33":"lib/forge-std/src/mocks/MockERC721.sol","34":"lib/forge-std/src/safeconsole.sol","35":"lib/openzeppelin-contracts/contracts/access/Ownable.sol","36":"lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol","37":"lib/openzeppelin-contracts/contracts/interfaces/IERC1363.sol","38":"lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol","39":"lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol","40":"lib/openzeppelin-contracts/contracts/interfaces/IERC5267.sol","41":"lib/openzeppelin-contracts/contracts/interfaces/draft-IERC6093.sol","42":"lib/openzeppelin-contracts/contracts/proxy/Clones.sol","43":"lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol","44":"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol","45":"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol","46":"lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol","47":"lib/openzeppelin-contracts/contracts/utils/Address.sol","48":"lib/openzeppelin-contracts/contracts/utils/Context.sol","49":"lib/openzeppelin-contracts/contracts/utils/Create2.sol","50":"lib/openzeppelin-contracts/contracts/utils/Errors.sol","51":"lib/openzeppelin-contracts/contracts/utils/Panic.sol","52":"lib/openzeppelin-contracts/contracts/utils/ShortStrings.sol","53":"lib/openzeppelin-contracts/contracts/utils/SlotDerivation.sol","54":"lib/openzeppelin-contracts/contracts/utils/StorageSlot.sol","55":"lib/openzeppelin-contracts/contracts/utils/Strings.sol","56":"lib/openzeppelin-contracts/contracts/utils/TransientSlot.sol","57":"lib/openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol","58":"lib/openzeppelin-contracts/contracts/utils/cryptography/EIP712.sol","59":"lib/openzeppelin-contracts/contracts/utils/cryptography/MessageHashUtils.sol","60":"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol","61":"lib/openzeppelin-contracts/contracts/utils/math/Math.sol","62":"lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol","63":"lib/openzeppelin-contracts/contracts/utils/math/SignedMath.sol","64":"lib/openzeppelin-contracts/contracts/utils/structs/BitMaps.sol","65":"lib/openzeppelin-contracts/contracts/utils/structs/Checkpoints.sol","66":"lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol","67":"script/deploy/DeployAztecL1Contracts.s.sol","68":"script/deploy/DeployRollupForUpgrade.s.sol","69":"script/deploy/DeployRollupLib.sol","70":"script/deploy/DeploymentConfiguration.sol","71":"script/deploy/RollupConfiguration.sol","72":"src/core/Rollup.sol","73":"src/core/RollupCore.sol","74":"src/core/interfaces/IEscapeHatch.sol","75":"src/core/interfaces/IFeeJuicePortal.sol","76":"src/core/interfaces/IInstance.sol","77":"src/core/interfaces/IRollup.sol","78":"src/core/interfaces/ISlasher.sol","79":"src/core/interfaces/IStaking.sol","80":"src/core/interfaces/IValidatorSelection.sol","81":"src/core/interfaces/IVerifier.sol","82":"src/core/interfaces/messagebridge/IInbox.sol","83":"src/core/interfaces/messagebridge/IOutbox.sol","84":"src/core/libraries/ConstantsGen.sol","85":"src/core/libraries/DataStructures.sol","86":"src/core/libraries/Errors.sol","87":"src/core/libraries/HatchMath.sol","88":"src/core/libraries/SlashPayloadLib.sol","89":"src/core/libraries/SlashRoundLib.sol","90":"src/core/libraries/StakingQueue.sol","91":"src/core/libraries/TimeLib.sol","92":"src/core/libraries/compressed-data/CheckpointLog.sol","93":"src/core/libraries/compressed-data/StakingQueueConfig.sol","94":"src/core/libraries/compressed-data/Tips.sol","95":"src/core/libraries/compressed-data/fees/FeeConfig.sol","96":"src/core/libraries/compressed-data/fees/FeeStructs.sol","97":"src/core/libraries/crypto/FrontierLib.sol","98":"src/core/libraries/crypto/Hash.sol","99":"src/core/libraries/crypto/MerkleLib.sol","100":"src/core/libraries/crypto/SampleLib.sol","101":"src/core/libraries/rollup/AttestationLib.sol","102":"src/core/libraries/rollup/BlobLib.sol","103":"src/core/libraries/rollup/EmpireSlasherDeploymentExtLib.sol","104":"src/core/libraries/rollup/EpochProofLib.sol","105":"src/core/libraries/rollup/FeeLib.sol","106":"src/core/libraries/rollup/InvalidateLib.sol","107":"src/core/libraries/rollup/ProposeLib.sol","108":"src/core/libraries/rollup/ProposedHeaderLib.sol","109":"src/core/libraries/rollup/RewardExtLib.sol","110":"src/core/libraries/rollup/RewardLib.sol","111":"src/core/libraries/rollup/RollupOperationsExtLib.sol","112":"src/core/libraries/rollup/STFLib.sol","113":"src/core/libraries/rollup/StakingLib.sol","114":"src/core/libraries/rollup/TallySlasherDeploymentExtLib.sol","115":"src/core/libraries/rollup/ValidatorOperationsExtLib.sol","116":"src/core/libraries/rollup/ValidatorSelectionLib.sol","117":"src/core/messagebridge/FeeJuicePortal.sol","118":"src/core/messagebridge/Inbox.sol","119":"src/core/messagebridge/Outbox.sol","120":"src/core/reward-boost/RewardBooster.sol","121":"src/core/slashing/EmpireSlashingProposer.sol","122":"src/core/slashing/Slasher.sol","123":"src/core/slashing/TallySlashingProposer.sol","124":"src/governance/Bn254LibWrapper.sol","125":"src/governance/CoinIssuer.sol","126":"src/governance/GSE.sol","127":"src/governance/GSEPayload.sol","128":"src/governance/Governance.sol","129":"src/governance/Registry.sol","130":"src/governance/RewardDistributor.sol","131":"src/governance/interfaces/IBn254LibWrapper.sol","132":"src/governance/interfaces/ICoinIssuer.sol","133":"src/governance/interfaces/IEmpire.sol","134":"src/governance/interfaces/IGovernance.sol","135":"src/governance/interfaces/IGovernanceProposer.sol","136":"src/governance/interfaces/IPayload.sol","137":"src/governance/interfaces/IProposerPayload.sol","138":"src/governance/interfaces/IRegistry.sol","139":"src/governance/interfaces/IRewardDistributor.sol","140":"src/governance/libraries/AddressSnapshotLib.sol","141":"src/governance/libraries/CheckpointedUintLib.sol","142":"src/governance/libraries/ConfigurationLib.sol","143":"src/governance/libraries/DepositDelegationLib.sol","144":"src/governance/libraries/Errors.sol","145":"src/governance/libraries/ProposalLib.sol","146":"src/governance/libraries/compressed-data/Ballot.sol","147":"src/governance/libraries/compressed-data/Configuration.sol","148":"src/governance/libraries/compressed-data/Proposal.sol","149":"src/governance/proposer/EmpireBase.sol","150":"src/governance/proposer/GovernanceProposer.sol","151":"src/mock/FeeAssetHandler.sol","152":"src/mock/MockVerifier.sol","153":"src/mock/MultiAdder.sol","154":"src/mock/StakingAssetHandler.sol","155":"src/mock/TestERC20.sol","156":"src/mock/libraries/BlobLib.sol","157":"src/mock/staking_asset_handler/MockZKPassportVerifier.sol","158":"src/periphery/DateGatedRelayer.sol","159":"src/periphery/RegisterNewRollupVersionPayload.sol","160":"src/periphery/SlashFactory.sol","161":"src/periphery/SlashPayload.sol","162":"src/periphery/SlashPayloadCloneable.sol","163":"src/periphery/interfaces/IDateGatedRelayer.sol","164":"src/periphery/interfaces/ISlashFactory.sol","165":"src/shared/interfaces/IMintableERC20.sol","166":"src/shared/libraries/BN254Lib.sol","167":"src/shared/libraries/CompressedTimeMath.sol","168":"src/shared/libraries/SignatureLib.sol","169":"src/shared/libraries/TimeMath.sol","170":"test/script/DeployAztecL1Contracts.t.sol","171":"test/script/DeployRollupForUpgrade.t.sol","172":"test/shouting.t.sol"},"language":"Solidity"}
|
|
@@ -143,7 +143,7 @@ contract DeploymentConfiguration is IDeploymentConfiguration, Test {
|
|
|
143
143
|
}),
|
|
144
144
|
votingDelay: Timestamp.wrap(3 * 24 * 60 * 60),
|
|
145
145
|
votingDuration: Timestamp.wrap(7 * 24 * 60 * 60),
|
|
146
|
-
executionDelay: Timestamp.wrap(
|
|
146
|
+
executionDelay: Timestamp.wrap(30 * 24 * 60 * 60),
|
|
147
147
|
gracePeriod: Timestamp.wrap(7 * 24 * 60 * 60),
|
|
148
148
|
quorum: 0.2e18,
|
|
149
149
|
requiredYeaMargin: 0.33e18,
|
|
@@ -71,7 +71,7 @@ contract RollupConfiguration is IRollupConfiguration, Test {
|
|
|
71
71
|
uint16 sequencerBps;
|
|
72
72
|
uint96 checkpointReward;
|
|
73
73
|
sequencerBps = 7000;
|
|
74
|
-
checkpointReward =
|
|
74
|
+
checkpointReward = 500e18;
|
|
75
75
|
|
|
76
76
|
return RewardConfig({
|
|
77
77
|
rewardDistributor: _rewardDistributor,
|
|
@@ -1,327 +1,73 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
// forge_broadcast.js - Reliable forge script broadcast with retry and timeout.
|
|
5
|
-
//
|
|
6
|
-
// Wraps `forge script` with:
|
|
7
|
-
// 1. --batch-size 8 to prevent forge broadcast hangs (forge bug with large RPC batches)
|
|
8
|
-
// 2. External timeout (forge's --timeout is unreliable for broadcast hangs)
|
|
9
|
-
// 3. Retry with --resume on real chains, or full retry from scratch on anvil
|
|
10
|
-
//
|
|
11
|
-
// Anvil's auto-miner has a race condition where batched transactions can get stranded
|
|
12
|
-
// in the mempool — they arrive after the auto-miner already triggered for the batch,
|
|
13
|
-
// and sit waiting for the next trigger that never comes. Neither evm_mine nor --resume
|
|
14
|
-
// can recover these stuck transactions. Interval mining (--block-time) avoids this issue.
|
|
15
|
-
//
|
|
16
|
-
// On anvil, we work around this by clearing broadcast artifacts and retrying from scratch.
|
|
17
|
-
// On real chains (where this anvil-specific bug doesn't apply), we use --resume.
|
|
2
|
+
// forge_broadcast.js — Run `forge script --broadcast` safely on anvil.
|
|
18
3
|
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
4
|
+
// Bug: anvil's auto-miner races with batched transactions. When a batch is sent,
|
|
5
|
+
// anvil mines a block for the first tx but the rest arrive after the auto-mine
|
|
6
|
+
// trigger fired, leaving them stranded or dropped. Temporarily switching to 1s
|
|
7
|
+
// interval mining for the duration of the broadcast avoids both variants.
|
|
21
8
|
//
|
|
22
|
-
//
|
|
23
|
-
// The wrapper adds those automatically.
|
|
9
|
+
// Only activates when anvil is in automine mode.
|
|
24
10
|
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
// --rpc-url "$RPC_URL" --private-key "$KEY" -vvv
|
|
28
|
-
//
|
|
29
|
-
// Environment variables:
|
|
30
|
-
// FORGE_BROADCAST_TIMEOUT - Override timeout per attempt in seconds (auto-detected from chain ID)
|
|
31
|
-
// FORGE_BROADCAST_MAX_RETRIES - Max retries after initial attempt (default: 3)
|
|
32
|
-
//
|
|
33
|
-
// Uses only Node.js built-ins (no external dependencies).
|
|
11
|
+
// Usage: ./scripts/forge_broadcast.js <forge script args...>
|
|
12
|
+
// (without --broadcast or --batch-size — added automatically)
|
|
34
13
|
|
|
35
14
|
import { spawn } from "node:child_process";
|
|
36
|
-
import {
|
|
37
|
-
|
|
38
|
-
// Chain IDs for timeout selection.
|
|
39
|
-
const MAINNET_CHAIN_ID = 1;
|
|
40
|
-
const SEPOLIA_CHAIN_ID = 11155111;
|
|
41
|
-
|
|
42
|
-
// Timeout per attempt: 300s for mainnet/sepolia (real chains are slow), 50s for everything else.
|
|
43
|
-
// FORGE_BROADCAST_TIMEOUT env var overrides the auto-detected value.
|
|
44
|
-
function getDefaultTimeout(chainId) {
|
|
45
|
-
if (chainId === MAINNET_CHAIN_ID || chainId === SEPOLIA_CHAIN_ID) return 300;
|
|
46
|
-
return 50;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const MAX_RETRIES = parseInt(
|
|
50
|
-
process.env.FORGE_BROADCAST_MAX_RETRIES ?? "3",
|
|
51
|
-
10,
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
if (!Number.isSafeInteger(MAX_RETRIES)) {
|
|
55
|
-
process.stderr.write(`MAX_RETRIES is not a valid integer.\n`);
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Batch size of 8 prevents forge from hanging during broadcast.
|
|
60
|
-
// See: https://github.com/foundry-rs/foundry/issues/6796
|
|
61
|
-
const BATCH_SIZE = 8;
|
|
62
|
-
const KILL_GRACE = 15_000;
|
|
63
|
-
// Exit code indicating a timeout, matching the `timeout` coreutil convention.
|
|
64
|
-
const EXIT_TIMEOUT = 124;
|
|
65
|
-
// Delay before retry to let pending transactions settle in the mempool.
|
|
66
|
-
const RETRY_DELAY = 10_000;
|
|
15
|
+
import { writeSync } from "node:fs";
|
|
67
16
|
|
|
68
|
-
|
|
69
|
-
process.stderr.write(`[forge_broadcast] ${msg}\n`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function sleep(ms) {
|
|
73
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Extract --rpc-url value from forge args. */
|
|
77
|
-
function extractRpcUrl(args) {
|
|
78
|
-
for (let i = 0; i < args.length - 1; i++) {
|
|
79
|
-
if (args[i] === "--rpc-url") return args[i + 1];
|
|
80
|
-
}
|
|
81
|
-
return undefined;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Strip --verify from args, returning the filtered args and whether --verify was present. */
|
|
85
|
-
function extractVerifyFlag(args) {
|
|
86
|
-
const filtered = args.filter((a) => a !== "--verify");
|
|
87
|
-
return { args: filtered, verify: filtered.length !== args.length };
|
|
88
|
-
}
|
|
17
|
+
const log = (msg) => process.stderr.write(`[forge_broadcast] ${msg}\n`);
|
|
89
18
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
/** JSON-RPC call using fetch. Rejects on JSON-RPC errors and timeouts. */
|
|
93
|
-
async function rpcCall(rpcUrl, method, params) {
|
|
94
|
-
const body = JSON.stringify({ jsonrpc: "2.0", id: 1, method, params });
|
|
95
|
-
const res = await fetch(rpcUrl, {
|
|
19
|
+
async function rpc(url, method, params = []) {
|
|
20
|
+
const res = await fetch(url, {
|
|
96
21
|
method: "POST",
|
|
97
22
|
headers: { "Content-Type": "application/json" },
|
|
98
|
-
body,
|
|
99
|
-
signal: AbortSignal.timeout(
|
|
23
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
|
|
24
|
+
signal: AbortSignal.timeout(10_000),
|
|
100
25
|
});
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const data = await res.text();
|
|
105
|
-
let parsed;
|
|
106
|
-
try {
|
|
107
|
-
parsed = JSON.parse(data);
|
|
108
|
-
} catch {
|
|
109
|
-
throw new Error(`Bad RPC response for ${method}: ${data.slice(0, 200)}`);
|
|
110
|
-
}
|
|
111
|
-
if (parsed.error) {
|
|
112
|
-
throw new Error(`RPC error for ${method}: ${JSON.stringify(parsed.error)}`);
|
|
113
|
-
}
|
|
114
|
-
return parsed.result;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Detect if the RPC endpoint is an anvil dev node via web3_clientVersion. */
|
|
118
|
-
async function detectAnvil(rpcUrl) {
|
|
119
|
-
try {
|
|
120
|
-
const version = await rpcCall(rpcUrl, "web3_clientVersion", []);
|
|
121
|
-
return version.toLowerCase().includes("anvil");
|
|
122
|
-
} catch {
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
26
|
+
const json = await res.json();
|
|
27
|
+
if (json.error) throw new Error(json.error.message);
|
|
28
|
+
return json.result;
|
|
125
29
|
}
|
|
126
30
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const result = await rpcCall(rpcUrl, "eth_chainId", []);
|
|
131
|
-
return parseInt(result, 16);
|
|
132
|
-
} catch {
|
|
133
|
-
return undefined;
|
|
134
|
-
}
|
|
31
|
+
function extractArg(args, flag) {
|
|
32
|
+
const i = args.indexOf(flag);
|
|
33
|
+
return i >= 0 && i < args.length - 1 ? args[i + 1] : undefined;
|
|
135
34
|
}
|
|
136
35
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const proc = spawn(
|
|
140
|
-
"forge",
|
|
141
|
-
["script", ...args, "--broadcast", "--batch-size", String(BATCH_SIZE)],
|
|
142
|
-
{
|
|
143
|
-
stdio: ["ignore", "pipe", "inherit"], // buffer stdout, pass stderr through
|
|
144
|
-
},
|
|
145
|
-
);
|
|
36
|
+
const args = process.argv.slice(2);
|
|
37
|
+
const rpcUrl = extractArg(args, "--rpc-url");
|
|
146
38
|
|
|
147
|
-
|
|
148
|
-
|
|
39
|
+
const [isAnvil, isAutomine] = rpcUrl
|
|
40
|
+
? await Promise.all([
|
|
41
|
+
rpc(rpcUrl, "web3_clientVersion").then((v) => v.toLowerCase().includes("anvil")).catch(() => false),
|
|
42
|
+
rpc(rpcUrl, "anvil_getAutomine").catch(() => false),
|
|
43
|
+
])
|
|
44
|
+
: [false, false];
|
|
149
45
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const timer = setTimeout(() => {
|
|
155
|
-
timedOut = true;
|
|
156
|
-
proc.kill("SIGTERM");
|
|
157
|
-
killTimer = setTimeout(() => proc.kill("SIGKILL"), KILL_GRACE);
|
|
158
|
-
}, timeoutSecs * 1000);
|
|
159
|
-
|
|
160
|
-
const finish = (code) => {
|
|
161
|
-
if (settled) return;
|
|
162
|
-
settled = true;
|
|
163
|
-
clearTimeout(timer);
|
|
164
|
-
clearTimeout(killTimer);
|
|
165
|
-
resolve({ exitCode: timedOut ? EXIT_TIMEOUT : code, stdout });
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
proc.on("error", () => finish(1));
|
|
169
|
-
proc.on("close", (code) => finish(code ?? 1));
|
|
170
|
-
});
|
|
46
|
+
if (isAnvil && isAutomine) {
|
|
47
|
+
await rpc(rpcUrl, "evm_setAutomine", [false]);
|
|
48
|
+
await rpc(rpcUrl, "evm_setIntervalMining", [1]);
|
|
171
49
|
}
|
|
172
50
|
|
|
173
|
-
|
|
51
|
+
const proc = spawn("forge", ["script", ...args, "--broadcast", "--batch-size", "8"], {
|
|
52
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
53
|
+
});
|
|
174
54
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
// and forge exits non-zero if ANY verification fails (crates/script/src/verify.rs), even when
|
|
178
|
-
// all transactions landed. We run verification as a separate step after broadcast succeeds.
|
|
179
|
-
const { args: forgeArgs, verify: wantsVerify } = extractVerifyFlag(
|
|
180
|
-
process.argv.slice(2),
|
|
181
|
-
);
|
|
182
|
-
const rpcUrl = extractRpcUrl(forgeArgs);
|
|
183
|
-
|
|
184
|
-
// Query chain info from RPC at startup.
|
|
185
|
-
const chainId = rpcUrl ? await getChainId(rpcUrl) : undefined;
|
|
186
|
-
const TIMEOUT = process.env.FORGE_BROADCAST_TIMEOUT
|
|
187
|
-
? parseInt(process.env.FORGE_BROADCAST_TIMEOUT, 10)
|
|
188
|
-
: getDefaultTimeout(chainId);
|
|
189
|
-
|
|
190
|
-
if (!Number.isSafeInteger(TIMEOUT)) {
|
|
191
|
-
process.stderr.write(`FORGE_BROADCAST_TIMEOUT is not a valid integer.\n`);
|
|
192
|
-
process.exit(1);
|
|
193
|
-
}
|
|
55
|
+
const stdout = [];
|
|
56
|
+
proc.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
194
57
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
);
|
|
58
|
+
const exitCode = await new Promise((resolve) => {
|
|
59
|
+
proc.on("error", () => resolve(1));
|
|
60
|
+
proc.on("close", (code) => resolve(code ?? 1));
|
|
61
|
+
});
|
|
198
62
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
log("Detected anvil — retries will reset chain instead of using --resume.");
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Run contract verification via `forge script --resume --verify --broadcast` (no timeout).
|
|
209
|
-
* Verification uses broadcast artifacts + re-compilation — it doesn't need simulation data.
|
|
210
|
-
* See: foundry-rs/foundry crates/script/src/build.rs (CompiledState::resume) and
|
|
211
|
-
* crates/script/src/verify.rs (verify_contracts).
|
|
212
|
-
* Failure is logged but doesn't affect the exit code — transactions already landed.
|
|
213
|
-
*/
|
|
214
|
-
async function runVerification(args) {
|
|
215
|
-
log("Running contract verification (no timeout)...");
|
|
216
|
-
const verifyResult = await new Promise((resolve) => {
|
|
217
|
-
const proc = spawn(
|
|
218
|
-
"forge",
|
|
219
|
-
["script", ...args, "--broadcast", "--resume", "--verify"],
|
|
220
|
-
{
|
|
221
|
-
stdio: ["ignore", "inherit", "inherit"],
|
|
222
|
-
},
|
|
223
|
-
);
|
|
224
|
-
let settled = false;
|
|
225
|
-
proc.on("error", () => {
|
|
226
|
-
if (!settled) {
|
|
227
|
-
settled = true;
|
|
228
|
-
resolve(1);
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
proc.on("close", (code) => {
|
|
232
|
-
if (!settled) {
|
|
233
|
-
settled = true;
|
|
234
|
-
resolve(code ?? 1);
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
if (verifyResult === 0) {
|
|
239
|
-
log("Contract verification succeeded.");
|
|
240
|
-
} else {
|
|
241
|
-
log(
|
|
242
|
-
`Contract verification failed (exit ${verifyResult}). Transactions are on-chain; verify manually if needed.`,
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/** Write buffered stdout to fd 1 (synchronous) and exit. */
|
|
248
|
-
function emitAndExit(result, code) {
|
|
249
|
-
const data = Buffer.concat(result.stdout);
|
|
250
|
-
if (data.length > 0) {
|
|
251
|
-
writeSync(1, data);
|
|
252
|
-
}
|
|
253
|
-
process.exit(code);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/** Run verification if requested, then emit stdout and exit. */
|
|
257
|
-
async function verifyAndExit(result) {
|
|
258
|
-
if (wantsVerify) {
|
|
259
|
-
await runVerification(forgeArgs);
|
|
260
|
-
}
|
|
261
|
-
emitAndExit(result, 0);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Attempt 1: initial broadcast
|
|
265
|
-
log(`Attempt 1/${MAX_RETRIES + 1}: broadcasting...`);
|
|
266
|
-
let result = await runForge(forgeArgs, TIMEOUT);
|
|
267
|
-
|
|
268
|
-
if (result.exitCode === 0) {
|
|
269
|
-
log("Broadcast succeeded on first attempt.");
|
|
270
|
-
await verifyAndExit(result);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
log(
|
|
274
|
-
`Attempt 1 ${result.exitCode === EXIT_TIMEOUT ? `timed out after ${TIMEOUT}s` : `failed (exit ${result.exitCode})`}.`,
|
|
275
|
-
);
|
|
276
|
-
|
|
277
|
-
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
278
|
-
log(`Waiting ${RETRY_DELAY / 1000}s before retry...`);
|
|
279
|
-
await sleep(RETRY_DELAY);
|
|
280
|
-
|
|
281
|
-
if (isAnvil) {
|
|
282
|
-
// On anvil: retry from scratch instead of --resume.
|
|
283
|
-
//
|
|
284
|
-
// Anvil's auto-miner has a race condition where batched transactions can arrive
|
|
285
|
-
// after the auto-miner already triggered, stranding them in the mempool. --resume
|
|
286
|
-
// just waits for these same stuck transactions and hangs again. A fresh retry
|
|
287
|
-
// re-simulates from current chain state and re-sends, which works because:
|
|
288
|
-
// - Forge computes new nonces from on-chain state
|
|
289
|
-
// - New transactions replace any stuck ones with the same nonce
|
|
290
|
-
// - The race condition is intermittent (~0.04%), so retries almost always succeed
|
|
291
|
-
rmSync("broadcast", { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
292
|
-
|
|
293
|
-
log(
|
|
294
|
-
`Attempt ${attempt + 1}/${MAX_RETRIES + 1}: retrying from scratch (anvil)...`,
|
|
295
|
-
);
|
|
296
|
-
result = await runForge(forgeArgs, TIMEOUT);
|
|
297
|
-
} else {
|
|
298
|
-
// On real chains: use --resume to pick up unmined transactions.
|
|
299
|
-
// --resume re-reads broadcast artifacts and resubmits unmined transactions.
|
|
300
|
-
// NOTE: --resume skips simulation, so console.log output (e.g. JSON deploy results)
|
|
301
|
-
// is only produced on the first attempt. We keep the first attempt's stdout (`result`)
|
|
302
|
-
// and only check the exit code from the --resume attempt.
|
|
303
|
-
log(`Attempt ${attempt + 1}/${MAX_RETRIES + 1}: --resume`);
|
|
304
|
-
const resumeResult = await runForge([...forgeArgs, "--resume"], TIMEOUT);
|
|
305
|
-
|
|
306
|
-
if (resumeResult.exitCode === 0) {
|
|
307
|
-
log(`Broadcast succeeded on attempt ${attempt + 1}.`);
|
|
308
|
-
// Emit the first attempt's stdout which has the JSON simulation output.
|
|
309
|
-
await verifyAndExit(result);
|
|
310
|
-
}
|
|
311
|
-
log(
|
|
312
|
-
`Attempt ${attempt + 1} ${resumeResult.exitCode === EXIT_TIMEOUT ? `timed out after ${TIMEOUT}s` : `failed (exit ${resumeResult.exitCode})`}.`,
|
|
313
|
-
);
|
|
314
|
-
continue;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if (result.exitCode === 0) {
|
|
318
|
-
log(`Broadcast succeeded on attempt ${attempt + 1}.`);
|
|
319
|
-
await verifyAndExit(result);
|
|
320
|
-
}
|
|
321
|
-
log(
|
|
322
|
-
`Attempt ${attempt + 1} ${result.exitCode === EXIT_TIMEOUT ? `timed out after ${TIMEOUT}s` : `failed (exit ${result.exitCode})`}.`,
|
|
323
|
-
);
|
|
63
|
+
if (isAnvil && isAutomine) {
|
|
64
|
+
try {
|
|
65
|
+
await rpc(rpcUrl, "evm_setIntervalMining", [0]);
|
|
66
|
+
await rpc(rpcUrl, "evm_setAutomine", [true]);
|
|
67
|
+
} catch {}
|
|
324
68
|
}
|
|
325
69
|
|
|
326
|
-
log(
|
|
327
|
-
|
|
70
|
+
log(exitCode === 0 ? "Broadcast succeeded." : `Broadcast failed (exit ${exitCode}).`);
|
|
71
|
+
const data = Buffer.concat(stdout);
|
|
72
|
+
if (data.length > 0) writeSync(1, data);
|
|
73
|
+
process.exit(exitCode);
|