Skip to content

Commit 387efd2

Browse files
committed
chore(solidity): adapt bytecode size gate to Sapphire 64 KiB limit
Sapphire's contract-size limit is being raised to 64 KiB. Split the size check by deployment target: Sapphire-side contracts (Accounting plus its delegatecall modules and library) cap at 64 KiB, while the Base-side bridge contracts (XRose, ROFLBridge) stay on the EIP-170 24576-byte cap. Hold a 1 KiB buffer below each limit, so enforced caps are 64512 (Sapphire) and 23552 (Base). Replace the project-internal Accounting target and the now-redundant Accounting headroom floor with the uniform buffer, and drop the stale optimizer-runs comment.
1 parent 516207e commit 387efd2

3 files changed

Lines changed: 38 additions & 24 deletions

File tree

solidity/hardhat.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ const config: HardhatUserConfig = {
5050
evmVersion: 'paris',
5151
optimizer: {
5252
enabled: true,
53-
// Keep bytecode size below EIP-170 limits; large "runs" can bloat size significantly.
5453
runs: 20,
5554
},
5655
viaIR: true,

solidity/scripts/.bytecode-baseline.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,5 @@
2323
"size": 16893
2424
}
2525
},
26-
"headroomFloor": {
27-
"Accounting": 200
28-
}
26+
"headroomFloor": {}
2927
}

solidity/scripts/check-bytecode-size.ts

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,38 @@ import { artifacts } from "hardhat";
22
import { promises as fs } from "node:fs";
33
import * as path from "node:path";
44

5-
const EIP170_LIMIT_BYTES = 24576;
6-
// Project-internal Accounting cap, well below EIP-170. Each new bridge
7-
// selector in `Accounting.fallback`'s allowlist costs ~23 B; leave room for
8-
// ~30+ more selectors before the next bump. Paired with the per-contract
9-
// headroom floor in `.bytecode-baseline.json::headroomFloor.Accounting`.
10-
const ACCOUNTING_TARGET_BYTES = 23800;
5+
// Per-chain contract-size limits enforced by each deploy target. Sapphire
6+
// allows 64 KiB; Base and other standard EVM chains stay at the EIP-170
7+
// 24576-byte cap.
8+
const SAPPHIRE_LIMIT_BYTES = 65536; // 64 KiB
9+
const EIP170_LIMIT_BYTES = 24576; // standard EVM (Base, etc.)
10+
11+
// Safety buffer held below each contract's real limit so nothing is ever
12+
// deployed right at the hard ceiling. The enforced cap is `limit - buffer`.
13+
const SIZE_BUFFER_BYTES = 1024; // 1 KiB
14+
15+
// Per-contract real limits; `required: true` makes a missing artifact a hard
16+
// failure (a rename/build regression), not a silent skip.
17+
const CONTRACTS: ReadonlyArray<{
18+
name: string;
19+
limit: number;
20+
required: boolean;
21+
}> = [
22+
// Sapphire-side: Accounting and its delegatecall modules/library.
23+
{ name: "Accounting", limit: SAPPHIRE_LIMIT_BYTES, required: true },
24+
{
25+
name: "AccountingHistoryModule",
26+
limit: SAPPHIRE_LIMIT_BYTES,
27+
required: true,
28+
},
29+
{ name: "BridgeModule", limit: SAPPHIRE_LIMIT_BYTES, required: true },
30+
{ name: "LockModule", limit: SAPPHIRE_LIMIT_BYTES, required: true },
31+
{ name: "BridgeLib", limit: SAPPHIRE_LIMIT_BYTES, required: true },
32+
// Base-side (base-sepolia, chainId 84532): standard EIP-170, NOT raised.
33+
// Optional — skipped silently until their artifacts exist.
34+
{ name: "XRose", limit: EIP170_LIMIT_BYTES, required: false },
35+
{ name: "ROFLBridge", limit: EIP170_LIMIT_BYTES, required: false },
36+
];
1137

1238
const BASELINE_PATH = path.join(__dirname, ".bytecode-baseline.json");
1339

@@ -162,20 +188,11 @@ async function main() {
162188
);
163189
}
164190

165-
// Hard targets first — Accounting must stay under the project-internal
166-
// ceiling. Accounting, BridgeModule, and BridgeLib are permanent: a
167-
// missing artifact for any of them is a hard failure (likely a
168-
// rename/build regression), not a silent skip.
169-
const results = [
170-
await check("Accounting", ACCOUNTING_TARGET_BYTES, baseline, true),
171-
await check("AccountingHistoryModule", EIP170_LIMIT_BYTES, baseline, true),
172-
await check("BridgeModule", EIP170_LIMIT_BYTES, baseline, true),
173-
await check("LockModule", EIP170_LIMIT_BYTES, baseline, true),
174-
await check("BridgeLib", EIP170_LIMIT_BYTES, baseline, true),
175-
// Optional bridge contracts; skipped silently until their artifacts exist.
176-
await check("XRose", EIP170_LIMIT_BYTES, baseline, false),
177-
await check("ROFLBridge", EIP170_LIMIT_BYTES, baseline, false),
178-
];
191+
const results = await Promise.all(
192+
CONTRACTS.map((c) =>
193+
check(c.name, c.limit - SIZE_BUFFER_BYTES, baseline, c.required),
194+
),
195+
);
179196

180197
if (updateMode) {
181198
await updateBaseline(results);

0 commit comments

Comments
 (0)