Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions .github/workflows/foundry.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: contracts

on:
push:
pull_request:

permissions:
contents: read

jobs:
foundry:
name: forge build / test / coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- uses: foundry-rs/foundry-toolchain@v1
with:
version: v1.8.5

- name: Build
run: forge build --sizes

- uses: actions/setup-node@v4
with:
node-version: 20
- name: Forge runtime == deploy runtime
run: npm ci && node --test test/forge-parity.test.mjs

# Tests named test_BUG_* deliberately fail: each one pins a known issue reported in the
# self-audit and stays red until the contract is fixed. They are run separately below.
- name: Test (unit, fuzz, invariant)
run: forge test -vvv --no-match-test test_BUG_
env:
FOUNDRY_PROFILE: ci

- name: Known-issue tests (expected to fail until fixed)
run: forge test --match-test test_BUG_ || true

- name: Coverage
run: |
forge coverage --no-match-test test_BUG_ --no-match-coverage "test/" --report summary --report lcov
- uses: actions/upload-artifact@v4
with:
name: lcov
path: lcov.info

bytecode:
name: JS tests (plan + bytecode match against live deployments)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm test

slither:
name: slither (informational)
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install slither + solc 0.8.26
run: |
pip install slither-analyzer==0.11.6 solc-select==1.2.0
solc-select install 0.8.26 && solc-select use 0.8.26
- name: Run slither on each contract with the deploy compiler settings
run: |
for f in contracts/*.sol; do
slither "$f" --compile-force-framework solc \
--solc-args "--optimize --optimize-runs 200 --evm-version cancun" \
--exclude-informational --exclude-optimization || true
done
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,11 @@ node_modules/
*.key
deployer.json
.DS_Store
# audit / foundry
.audit-venv/
.audit-venv-myth/
out-foundry/
cache-foundry/
lcov.info
gambit_out/
cache/
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "foundry-lib/forge-std"]
path = foundry-lib/forge-std
url = https://github.com/foundry-rs/forge-std
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,26 @@ Verification tries these in order:

Anything still unverified is written into `deployments/X.json` as a TODO. The standard-JSON input for manual verification goes to `deployments/verify/`.

## Tests

```bash
git submodule update --init # forge-std
npm test # plan + live bytecode match (+ forge parity if built)
forge build && forge test --no-match-test test_BUG_ # unit, fuzz, invariant
forge coverage --no-match-test test_BUG_ --no-match-coverage "test/" --report summary
```

`test/foundry/` holds unit tests for every external function and revert path of all six contracts,
fuzz tests, and handler-based invariant suites (escrow/bond conservation, no double payout, no
reclaim inside the window, write-once verdicts and filings, role checks). Line, statement, branch and
function coverage is 100%. `foundry.toml` uses the same compiler settings as `lib/compile.mjs`, and
`test/forge-parity.test.mjs` checks that forge's runtime bytecode equals the deployed runtime
(metadata hash excluded), so the suite tests exactly what is live.

Tests named `test_BUG_*` fail on purpose: each one pins an open self-audit finding and stays red
until the contract is fixed. Tests named `test_finding_*` pass and document current behaviour that is
a design or operational risk.

## ERC-8004 registries

The official ERC-8004 v2.0.0 registries sit at the same addresses wherever they exist. `getVersion()` returns `2.0.0`.
Expand Down
265 changes: 265 additions & 0 deletions deployments/rootstock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
{
"chain": "rootstock",
"chainId": 30,
"deployer": "0x5d016C1B4d7A42eB7c982da1Ae7fFdB958CFCAe9",
"operator": "0x5d016C1B4d7A42eB7c982da1Ae7fFdB958CFCAe9",
"setVersion": "2026-10-16",
"compiler": {
"solc": "v0.8.26+commit.8a97fa7a",
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "cancun"
}
},
"deployedAt": "2026-10-23T10:47:24.835Z",
"explorer": "https://explorer.rootstock.io",
"contracts": {
"PredgeAgentValidator": {
"name": "PredgeAgentValidator",
"address": "0x925767fdce6C819F027f55CC50464e3bCdfD2671",
"nonce": 0,
"args": [
"0x5d016C1B4d7A42eB7c982da1Ae7fFdB958CFCAe9"
],
"constructorArgs": "0x0000000000000000000000005d016c1b4d7a42eb7c982da1ae7ffdb958cfcae9",
"sourceSha256": "7c452fef0a77be8d886fbf251d918db95f1ac851268c2e3c13ed7c65ae9fee56",
"source": "predge-arc@0b48435 (= erc8004-outcome-validator monad/contracts)",
"txHash": "0x6fbbe762af5ea3bf52643c52590c4b85cb4c5bd5cb5ca81648d46dcf4fad05c1",
"block": 9349449,
"gasUsed": "912160",
"effectiveGasPrice": "26065600",
"verified": {
"ok": true,
"via": "sourcify",
"match": "exact_match"
}
},
"AgentJob": {
"name": "AgentJob",
"address": "0xcBaB4a9FA4d756b4C5e2a18F055d2E0132CE7D1D",
"nonce": 1,
"args": [],
"constructorArgs": "0x",
"sourceSha256": "9d958d5266d1876e16f95f26b2b437be32e2f45fe8bf10879ab93eea9a8ccba5",
"source": "predge-arc@0b48435",
"txHash": "0x96fbccebb7be61a31b58e59a88eaabee4468591a7f19a77c0265c470a720a5f9",
"block": 9349450,
"gasUsed": "569497",
"effectiveGasPrice": "26065600",
"verified": {
"ok": true,
"via": "sourcify",
"match": "exact_match"
}
},
"PredgeValidatorBond": {
"name": "PredgeValidatorBond",
"address": "0x800B5596a8540e752dE8f8a8AF99E927d139A643",
"nonce": 2,
"args": [
"0x5d016C1B4d7A42eB7c982da1Ae7fFdB958CFCAe9",
"0xcBaB4a9FA4d756b4C5e2a18F055d2E0132CE7D1D",
"86400"
],
"constructorArgs": "0x0000000000000000000000005d016c1b4d7a42eb7c982da1ae7ffdb958cfcae9000000000000000000000000cbab4a9fa4d756b4c5e2a18f055d2e0132ce7d1d0000000000000000000000000000000000000000000000000000000000015180",
"sourceSha256": "085e2deaf0c32a45ff4de240215f68e8c80841ac135d762f5ddea421f46b064a",
"source": "predge-robinhood@bc57c52 = predge-arc@c0762ea (v2.1, audit fix F1)",
"txHash": "0x4bb954dc7b2a090e376ea208653d0f58728729cf1c30d283660d2672ee4b554b",
"block": 9349452,
"gasUsed": "1046606",
"effectiveGasPrice": "26065600",
"verified": {
"ok": true,
"via": "sourcify",
"match": "exact_match"
}
},
"PredgeSettlement": {
"name": "PredgeSettlement",
"address": "0x47BE8a7d461De505723a0Ac1d74982e53f8E412b",
"nonce": 3,
"args": [],
"constructorArgs": "0x",
"sourceSha256": "fca0ec609a3374d93ee68e973618ae675d93abf43fd5bea3f03b653f23690711",
"source": "predge-arc@6f29672",
"txHash": "0xe600e2581b743056232267bd6ec404e9e3efc5df20c5553086c51c0e057a8806",
"block": 9349453,
"gasUsed": "272960",
"effectiveGasPrice": "26065600",
"verified": {
"ok": true,
"via": "sourcify",
"match": "exact_match"
}
},
"MockRefundProtocol": {
"name": "MockRefundProtocol",
"address": "0xcaC30945CDF0F963f38972d939e111B87b80Cf79",
"nonce": 4,
"args": [
"0xb2972B4944Eec83946EB7B23D572AbE04F46E22c"
],
"constructorArgs": "0x000000000000000000000000b2972b4944eec83946eb7b23d572abe04f46e22c",
"sourceSha256": "c37ac127c5eb469af4e9adf7293e4a3db6c483e178efc2cc735fc3b99c841230",
"source": "predge-arc@293bd70",
"txHash": "0x69250f2bbd46bd304b9aa03685d9fc0dd5f3a04fef19ae0dcf7657d60da86764",
"block": 9349454,
"gasUsed": "268629",
"effectiveGasPrice": "26065600",
"verified": {
"ok": true,
"via": "sourcify",
"match": "exact_match"
}
},
"PredgeRefundArbiter": {
"name": "PredgeRefundArbiter",
"address": "0xb2972B4944Eec83946EB7B23D572AbE04F46E22c",
"nonce": 5,
"args": [
"0xcaC30945CDF0F963f38972d939e111B87b80Cf79",
"0x5d016C1B4d7A42eB7c982da1Ae7fFdB958CFCAe9",
"86400",
"1000000000000000"
],
"constructorArgs": "0x000000000000000000000000cac30945cdf0f963f38972d939e111b87b80cf790000000000000000000000005d016c1b4d7a42eb7c982da1ae7ffdb958cfcae9000000000000000000000000000000000000000000000000000000000001518000000000000000000000000000000000000000000000000000038d7ea4c68000",
"sourceSha256": "4a2c59b0067800de698f33c10e64c14441c2cc56004a94d39e0d4eea90e6c465",
"source": "predge-arc@95b723d (v2, audit fix F2)",
"txHash": "0x1cf5b6529ae2452dd5e51422fca794900d001d01d6011b8ef3e20d34989d5c52",
"block": 9349456,
"gasUsed": "1139332",
"effectiveGasPrice": "26065600",
"verified": {
"ok": true,
"via": "sourcify",
"match": "exact_match"
}
}
},
"erc8004": {
"IdentityRegistry": null,
"ReputationRegistry": null,
"ValidationRegistry": null
},
"smoke": [
{
"check": "PredgeAgentValidator runtime == compiled",
"ok": true,
"detail": "0x925767fdce6C819F027f55CC50464e3bCdfD2671"
},
{
"check": "AgentJob runtime == compiled",
"ok": true,
"detail": "0xcBaB4a9FA4d756b4C5e2a18F055d2E0132CE7D1D"
},
{
"check": "PredgeValidatorBond runtime == compiled",
"ok": true,
"detail": "0x800B5596a8540e752dE8f8a8AF99E927d139A643"
},
{
"check": "PredgeSettlement runtime == compiled",
"ok": true,
"detail": "0x47BE8a7d461De505723a0Ac1d74982e53f8E412b"
},
{
"check": "MockRefundProtocol runtime == compiled",
"ok": true,
"detail": "0xcaC30945CDF0F963f38972d939e111B87b80Cf79"
},
{
"check": "PredgeRefundArbiter runtime == compiled",
"ok": true,
"detail": "0xb2972B4944Eec83946EB7B23D572AbE04F46E22c"
},
{
"check": "validator.owner == deployer",
"ok": true,
"detail": "0x5d016C1B4d7A42eB7c982da1Ae7fFdB958CFCAe9"
},
{
"check": "validator.validator == operator",
"ok": true,
"detail": "0x5d016C1B4d7A42eB7c982da1Ae7fFdB958CFCAe9"
},
{
"check": "validator.getValidationStatus(unknown) is empty",
"ok": true,
"detail": "0x0000000000000000000000000000000000000000"
},
{
"check": "job.jobCount readable",
"ok": true,
"detail": "0"
},
{
"check": "job.createJob(value 0) reverts ZeroEscrow (eth_call)",
"ok": true,
"detail": ""
},
{
"check": "bond.owner == deployer",
"ok": true,
"detail": "0x5d016C1B4d7A42eB7c982da1Ae7fFdB958CFCAe9"
},
{
"check": "bond.validator == operator",
"ok": true,
"detail": "0x5d016C1B4d7A42eB7c982da1Ae7fFdB958CFCAe9"
},
{
"check": "bond.job == AgentJob",
"ok": true,
"detail": "0xcBaB4a9FA4d756b4C5e2a18F055d2E0132CE7D1D"
},
{
"check": "bond.disputeWindow",
"ok": true,
"detail": "86400"
},
{
"check": "bond.wouldSlash(unknown) == false",
"ok": true,
"detail": ""
},
{
"check": "settlement.owner == deployer",
"ok": true,
"detail": "0x5d016C1B4d7A42eB7c982da1Ae7fFdB958CFCAe9"
},
{
"check": "arbiter.owner == deployer",
"ok": true,
"detail": "0x5d016C1B4d7A42eB7c982da1Ae7fFdB958CFCAe9"
},
{
"check": "arbiter.operator == operator",
"ok": true,
"detail": "0x5d016C1B4d7A42eB7c982da1Ae7fFdB958CFCAe9"
},
{
"check": "arbiter.refundProtocol == mock",
"ok": true,
"detail": "0xcaC30945CDF0F963f38972d939e111B87b80Cf79"
},
{
"check": "mock.arbiter == arbiter",
"ok": true,
"detail": "0xb2972B4944Eec83946EB7B23D572AbE04F46E22c"
},
{
"check": "arbiter.challengeWindow/minBond",
"ok": true,
"detail": ""
},
{
"check": "arbiter.rulingOf(0) empty",
"ok": true,
"detail": ""
}
],
"legacy": null
}
1 change: 1 addition & 0 deletions foundry-lib/forge-std
Submodule forge-std added at 8e4051
Loading
Loading