ID: task_240626_0b
Assigned to: Robbert Abimbola
Created: 2026-06-24
Status: Open (PR#13 submitted — pending merge)
Roadmap ref: P3 — Security, Integration, and Testnet Readiness (contract upgrade flows)
Option chosen: Option A — Upgradeable platform contracts
PR: #13 (robertocarlous:feature/platform-upgradeable)
DIN (InfiniteZero) is a federated-learning network coordinated by Solidity contracts on Optimism Sepolia. Raw training data never leaves a participant's machine — only model artifacts (pinned to IPFS) and on-chain coordination state are shared. Participants run dincli, a Python/Typer CLI, to act as clients (local training), auditors (scoring), or aggregators (combining models).
The contract layer has two tiers:
- Platform-level contracts (deployed once by the DIN-Representative):
DinCoordinator,DinToken,DinValidatorStake,DINModelRegistry— all underhardhat/contracts/. - Task-level contracts (deployed per model by the model owner):
DINTaskCoordinator,DINTaskAuditor.
- All 4 platform contracts exist, are deployed, and work as plain (non-upgradeable) contracts: constructor-based init,
Ownable(or a bespokedaoAdminfield onDINModelRegistry), some immutable cross-contract references (e.g.DinValidatorStakeholds immutableDIN_TOKEN/DIN_COORDINATORaddresses). - Test coverage on the 4 contracts was thin — only
hardhat/test/DinValidatorStake.test.tsexisted. The other 3 had no dedicated tests. - Working-draft design doc:
Developer/issues/upgradable-contracts.md(treat as draft — call out stale/inconsistent items).
Convert the 4 platform-level contracts from plain/constructor-based to an upgradeable pattern, with tests proving upgrades preserve state and access control.
- Pick a proxy pattern (UUPS or Transparent Proxy) — document why.
- Convert all 4 contracts: replace constructors with
initialize()functions (Initializable), remove/relocate anyimmutablecross-contract references, audit storage layout (document the rules followed). - Add Hardhat deploy scripts for proxy deployment of each contract (initial deploy + upgrade flow).
- Add upgrade-safety tests per contract: state persists across an upgrade, access control still holds post-upgrade, storage-layout regressions caught (via
hardhat-upgradesvalidateUpgrade()). - Short write-up: what changed per contract, the proxy pattern chosen and why, and any deviation from the existing
Ownable/daoAdminaccess model.
- Governance/DAO redesign, task-level contracts (
DINTaskCoordinator/DINTaskAuditor), changes todincli.
- Proxy pattern chosen and justified — Transparent Proxy (OpenZeppelin
@openzeppelin/hardhat-upgrades,PROXY_KIND = "transparent") - All 4 contracts converted to initializer pattern, storage-layout-safe
- Hardhat deploy scripts for proxy deploy + upgrade —
hardhat/scripts/deploy-platform.ts,hardhat/scripts/upgrade-platform.ts - Upgrade-safety tests per contract — 12 tests (3 per contract): state persistence, access control,
validateUpgrade()storage checkhardhat/test/DinToken.upgrade.test.tshardhat/test/DinCoordinator.upgrade.test.tshardhat/test/DinValidatorStake.upgrade.test.tshardhat/test/DINModelRegistry.upgrade.test.ts
- Write-up of changes and proxy pattern rationale — in PR#13 description
- Doc-inaccuracy notes from
upgradable-contracts.md— listed in PR#13 description
Proxy pattern: Transparent Proxy over UUPS because upgrade authority is separated from implementation logic — a bad V2 cannot brick the proxy by omitting upgrade functions. Fits the current model where the DIN-Representative controls upgrades; ProxyAdmin ownership can transfer to a multisig later.
Deployment order (proxies cannot use nested new Contract()):
- Deploy
DinTokenproxy →initialize() - Deploy
DinCoordinatorproxy →initialize(dinTokenAddress) DinToken.setCoordinator(dinCoordinatorAddress)- Deploy
DinValidatorStakeproxy →initialize(dinTokenAddress, dinCoordinatorAddress) DinCoordinator.updateValidatorStakeContract(stakeAddress)- Deploy
DINModelRegistryproxy →initialize(stakeAddress)
Access model deviation: DINModelRegistry's bespoke daoAdmin / setDAOAdmin / DAOAdminUpdated removed in favour of standard OZ OwnableUpgradeable (owner() / transferOwnership()). dincli ABIs referencing the old interface will need a follow-up update when testnet is redeployed (out of scope for this PR).
Storage layout rules: no reordering/removal of existing variables; new variables only appended; immutable cross-contract refs replaced with regular storage; hardhat-upgrades validateUpgrade() run in tests before each upgrade.
- Platform contracts used constructors — now converted to
initialize(). DinTokenimmutableOWNER— nowcoordinatorstorage.DinCoordinatorimmutabledinToken+ inlinenew DinToken()— now external token proxy + storage ref.DinValidatorStakeimmutableDIN_TOKEN/DIN_COORDINATOR— now storage.DINModelRegistryuses mutabledaoAdminbut is not proxy-based — now proxy +OwnableUpgradeable.hardhat/test/DinValidatorStake.test.tsdescribed as existing — it did not exist ondevelop; PR#13 adds the first test suite.hardhat-upgradestoolchain missing from doc — added in PR#13.- Deploy scripts for platform proxies missing from doc — added
deploy-platform.tsandupgrade-platform.ts.
cd hardhat && npm testExpected: 12 passing upgrade-safety tests (3 per platform contract).
PR#13 open against develop: #13
Branch: robertocarlous:feature/platform-upgradeable