From 468b286e9e61be2e378a6de25082a0cb2c6b8b13 Mon Sep 17 00:00:00 2001 From: Zvonimir Date: Mon, 4 May 2026 16:36:02 +0200 Subject: [PATCH 1/2] fix(devnet): escape backticks in node -e JS comments Five backticks inside JS comments at lines 742, 868, 881, 898, 899 were not escaped, so bash interpreted them as command substitution inside the surrounding `node -e "..."` double-quoted string. On every `devnet start` this produced noisy errors like: scripts/devnet.sh: line 694: continue: only meaningful in a `for'... scripts/devnet.sh: line 694: nonce++: command not found scripts/devnet.sh: line 694: syntax error near unexpected token `idId' The text was inside JS `//` comments so the staking script still ran, but the diagnostics drowned out real failures (e.g. the stale-bytecode revert this commit's sibling fixes). Lines 730-733 and 768 already use `\`` correctly; these five were oversights. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/devnet.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/devnet.sh b/scripts/devnet.sh index c89acfed7..a1576c203 100755 --- a/scripts/devnet.sh +++ b/scripts/devnet.sh @@ -739,7 +739,7 @@ cmd_start() { const opSigners = new Array(n).fill(null); const nodeRoles = new Array(n).fill('edge'); // Codex round 4 on PR #368: read config.json FIRST and INDEPENDENTLY - // of wallets.json. The previous loop did wallets first and `continue`d + // of wallets.json. The previous loop did wallets first and \`continue\`d // on parse failure BEFORE reading config, so an intended core whose // wallets.json was malformed silently kept the 'edge' default, // dropped out of coreIdxs, and the lostCores guard never fired. @@ -865,7 +865,7 @@ cmd_start() { // this code path exists to prevent. // // Codex round 2 on PR #368: re-read 'pending' before EVERY tx - // (not once per node loop). The previous code did `nonce++` at + // (not once per node loop). The previous code did \`nonce++\` at // call site so a failed approve advanced the local counter even // though the chain nonce did not, leaving createConviction + // updateAsk to send with an inflated nonce that would either @@ -878,7 +878,7 @@ cmd_start() { return tx.wait(); }; // Codex round 4 on PR #368: skip createConviction if the daemon - // already opened a position. PR 366 wired `EVMChainAdapter.ensureProfile()` + // already opened a position. PR 366 wired \`EVMChainAdapter.ensureProfile()\` // to open a default 50k V10 conviction during agent startup, so by // the time this script reaches the staking loop the daemon has // (usually) already staked. Running createConviction again would @@ -895,8 +895,8 @@ cmd_start() { // updateAsk is INDEPENDENT of stake state, so we still attempt // it below regardless of the probe outcome. // - // Codex round 7 on PR #368: probe `getNodeStakeV10(idId)` instead - // of NFT `balanceOf(opSigner.address)`. The op wallet could hold + // Codex round 7 on PR #368: probe \`getNodeStakeV10(idId)\` instead + // of NFT \`balanceOf(opSigner.address)\`. The op wallet could hold // positions for OTHER identities (not in our devnet today, but // a wallet-scoped probe is the wrong semantic check); we want // to know whether THIS identity is staked. From 27490f2bc2a45337268baf958d8c388d3f5eb79b Mon Sep 17 00:00:00 2001 From: Zvonimir Date: Mon, 4 May 2026 16:43:53 +0200 Subject: [PATCH 2/2] fix(devnet): redeploy contracts when artifacts outpace running chain When hardhat is left running across edits to .sol sources, the on-chain bytecode lags the recompiled artifacts on disk. The next `devnet start` short-circuits in `start_hardhat` (alive PID), then `deploy_contracts` skips on the still-present `.devnet/hardhat/deployed` marker, so daemons connect to addresses whose code predates the latest source. View methods added since that deploy revert with "function selector not recognized"; ethers surfaces this as `require(false)` (no return data, no fallback). On a 6-node devnet this manifests as `0/4 core node(s) staked` because the staking loop's `CSS.getNodeStakeV10` probe fails for every node. Detect the mismatch by comparing artifact mtimes to the marker. If any contract artifact JSON is newer than the marker, kill the running hardhat so the existing fresh-start path (which clears the marker plus `localhost_contracts.json` + `hardhat_contracts.json`) runs and `deploy_contracts` re-deploys onto a fresh chain. No-op when artifacts are unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/devnet.sh | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/scripts/devnet.sh b/scripts/devnet.sh index a1576c203..2d1c24654 100755 --- a/scripts/devnet.sh +++ b/scripts/devnet.sh @@ -101,10 +101,36 @@ ensure_built() { start_hardhat() { local pidfile="$DEVNET_DIR/hardhat.pid" + local marker="$DEVNET_DIR/hardhat/deployed" + local artifacts_dir="$REPO_ROOT/packages/evm-module/artifacts/contracts" if [ -f "$pidfile" ] && kill -0 "$(cat "$pidfile")" 2>/dev/null; then - log "Hardhat node already running (PID $(cat "$pidfile"))" - return 0 + # Stale-bytecode guard. When contracts are recompiled while hardhat + # keeps running, the on-chain bytecode lags the artifacts on disk. + # The next `devnet start` short-circuits hardhat reuse here, then + # `deploy_contracts` skips on the still-present marker, so daemons + # connect to addresses whose code is older than the source. View + # methods added since the live deploy revert with "function selector + # not recognized" (e.g. CSS.getNodeStakeV10 in the staking loop). + # Detect the mismatch by comparing artifact mtimes to the marker; + # if any artifact is newer, kill hardhat so the fresh-start path + # below clears the marker + deployments and `deploy_contracts` + # re-deploys onto a fresh chain. + if [ -f "$marker" ] && [ -d "$artifacts_dir" ] \ + && [ -n "$(find "$artifacts_dir" -name '*.json' -newer "$marker" -print -quit 2>/dev/null)" ]; then + local stale_pid + stale_pid=$(cat "$pidfile") + log "Hardhat (PID $stale_pid) holds outdated contracts (artifacts newer than deploy marker) — restarting for fresh deploy" + kill "$stale_pid" 2>/dev/null || true + for _ in 1 2 3 4 5; do + kill -0 "$stale_pid" 2>/dev/null || break + sleep 1 + done + rm -f "$pidfile" + else + log "Hardhat node already running (PID $(cat "$pidfile"))" + return 0 + fi fi log "Starting Hardhat node on port $HARDHAT_PORT..."