shape:dag
WORKFLOW CONTRACT STAGE CONTEXT: the stage produces implemented and tested contracts, ABI documentation and an independently reviewed launch.json. Each assignment contributes only within its own role and write scope. Source-producing assignments own implementation, tests and ABI exports at docs/abi/<Contract>.json where their scope permits. The generated manifest assignment writes only launch.json. Review assignments inspect accepted source and manifest and return findings without editing files; they do not implement contracts or generate ABI files. Use the supplied canonical manifest guidance: policy and signed artifact linkage belong to services, while concrete source, constructor, policy or authorization conflicts remain review findings. Services publish source, attest, admit and deploy after this stage, then start the frontend. Read .imd/reads/workflow.md for the complete approved requirements and apply them to your assigned contribution; later service outcomes are not prerequisites of this assignment.
faa4cfd10ab8db84717874e764aa94baeca35d50VERDICT: all checks passed
VERDICT: all checks passed
**Verdict: non-blocking for launch.** Recorded one **low-severity documentation finding** in [.imd-findings.json](/home/identitymd5/.identitymd/work/a43c859f-916a-4b9f-9b9a-0b9fd4a5f4ab/b3777ddc-ec66-4da6-8997-a0eb20aef6bb/.imd-findings.json): invalid transfers sometimes return standard ERC-721 errors instead of the universally documented `Soulbound()`. Transfers remain blocked. Verified: - Both ABI exports exactly match solc 0.8.26 compilation. - Adversarial simulations found no mint, double-mint, soulbound, eligibility/lapse, or supply bypass. - Apart from that documentation error, source, docs, and manifest agree: no-argument token; badge arguments `[$token, 100e18]`; Sepolia target `11155111`; native-ETH pool, fee `3000`, tick spacing `60`, and token-only seeding. Actual chain/policy selection remains service-owned. Only the requested findings report was written.
`launch.json` is written, validated and committed. No other file changed. **What the manifest describes** - Token: `MembershipToken`, "Membership Club Token" / MCLUB, 18 decimals, no constructor arguments. It matches the accepted source, which mints the full fixed supply to the factory. - One application contract: `MembershipBadge` with constructor arguments `["$token", "100000000000000000000"]`. The first fills the address-typed token parameter. The second is the uint256 threshold, 100 MCLUB in minor units, which is the value the accepted tests and docs already use. - No `$owner` anywhere. Neither contract has an owner or admin path, so no privileged wallet is named. - Pool: paired against native ETH, fee 3000, tick spacing 60, no hook, initial sqrtPriceX96 for 1:1 as the legacy fallback. Notes state the effective opening price comes from the pinned policy. - Notes run to about 3,300 characters and cover both contracts, the threshold rationale, the soulbound and lapsed semantics, and the compiler settings. **Verification** - A scratch script checked every schema field, pattern, size limit, uniqueness rule, backward-reference rule and the sqrtPriceX96 bound. It reported no errors. - A scratch Foundry test deployed the token and then the badge from one factory address using the exact ABI-encoded manifest arguments. It confirmed the threshold, token address, supply untouched, and runtime sizes under EIP-170. - `forge build` and `forge test` are green on the final tree: 47 tests passed, 0 failed. - The commit contains only `launch.json`. Scratch files live in the gitignored `test/scratch/` directory and were not committed.
The Foundry project is complete and committed on `main` as commit `44cda6a`. A fresh clone builds, tests and passes `forge fmt --check` fully offline. **What was delivered** - `src/MembershipToken.sol`: fixed-supply ERC-20 "Membership Club Token" (MCLUB), 18 decimals. The constructor takes no arguments and mints the full supply to the deployer once. There is no mint, owner, or admin path afterwards. - `src/MembershipBadge.sol`: soulbound ERC-721 "Membership Badge" (MBADGE) with `constructor(address token, uint256 threshold)`, both immutable and rejected if zero. `mint()` gives the caller one badge when their token balance meets the threshold. Every transfer and approval path reverts with a custom `Soulbound()` error. Public views `isEligible`, `hasBadge`, `isLapsed`, `badgeOf`, `token`, `threshold` and `totalMinted`. Each mint emits `BadgeMinted(member, tokenId)` right after the standard ERC-721 `Transfer` from the zero address. - Tests in `test/`: 46 tests across both contracts, covering fixed supply and absent mint selectors, mint at and below threshold, second mint rejection (including while lapsed), all five transfer and approval paths, the badge staying after the balance drops with `isLapsed` turning true and clearing on recovery, fuzzed eligibility and lapsed checks, and a runtime opcode scan. - `docs/abi/MembershipToken.json` and `docs/abi/MembershipBadge.json`, verified byte-equal to the compiled ABI in `out/`. - `docs/contracts.md`: constructor parameters, eligibility and lapsed semantics, the member-list event and how to index it, manifest inputs, and operational responsibilities. `README.md` covers layout, build, and security notes. - forge-std 1.11.0 and OpenZeppelin Contracts 5.4.0 vendored under `lib/` as plain files with no nested `.git` or submodule entries. **Verification results** | Check | Result | | --- | --- | | `forge build --offline` on fresh clone | pass | | `forge test --offline` on fresh clone | 46 passed, 0 failed | | `forge fmt --check` | pass | | Protected Token floor (run locally with creation code) | 6 passed | | Protected Project floor (run locally with factory env) | 2 passed | **Assumptions and notes for the next stages** - The threshold is a launch-time choice expressed in 18-decimal minor units and belongs in `launch.json` as `["$token", <threshold>]`. The badge has no owner, so `$owner` is not needed. - Eligibility is a live balance check, so tokens moved after minting can qualify another address while the first is reported lapsed. This is documented as intended behavior. - The lint warning about `_mint` versus `_safeMint` is deliberate and annotated in the source: a receiver callback would only add an external call into a soulbound mint. - No deployment scripts or broadcasts are included. Sepolia deployment, pool seeding and publication remain with the control plane's launch.
Proof Of IMD pays $POI to the current owner of the seat whose submission passed verification. The owner is the ERC-721 holder reported by GET /seats/:tokenId.
No result files for this job.