Skip to content

SDK #20 (draft): dincli/sdk foundation — loaders + manifest/runtime + state/serialize - #31

Draft
Santiagocetran wants to merge 36 commits into
InfiniteZeroFoundation:feat/din-sdkfrom
Santiagocetran:feat/din-sdk
Draft

SDK #20 (draft): dincli/sdk foundation — loaders + manifest/runtime + state/serialize#31
Santiagocetran wants to merge 36 commits into
InfiniteZeroFoundation:feat/din-sdkfrom
Santiagocetran:feat/din-sdk

Conversation

@Santiagocetran

@Santiagocetran Santiagocetran commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Draft / WIP — progress on #20, not a merge-ready PR. Opened early per the request to keep feature-branch work visible. Base is feat/din-sdk (not develop).

What this contains

Incremental extraction of shared logic into dincli/sdk/ (issue #20), landed in reviewable slices:

  1. Interface proposal (Developer/design/sdk-interface-proposal.md, v0.3) — SDK public interface + JSON output conventions; candidate seed for P3-DOC7. Direction confirmed by @umeradl; contract not yet frozen.
  2. Package seeddincli/sdk/{cid,errors}.py + a fresh-subprocess import-boundary test.
  3. Loader tierdincli/sdk/{config,log,web3}.py{ipfs,contracts}.py{manifest,runtime}.py, with the original modules (cli/utils.py, cli/log.py, services/{ipfs,runtime}.py, cli/contract_utils.py) reduced to backward-compat re-export shims.
  4. State + serialization layer (this task — task_220726_7 Part A)dincli/sdk/state.py and dincli/sdk/serialize.py (details below).

The dincli/sdk package now holds a dependency-clean foundation: cid, errors, config, log, web3, ipfs, contracts, manifest, runtime, state, serialize. An import-boundary test enforces the core rule — import dincli.sdk pulls in no typer, rich, or dincli.cli.* (CLI depends on SDK, never the reverse).

Latest slice — state.py + serialize.py (task_220726_7 Part A)

  • dincli/sdk/state.py — GI-state converters (GIstateToDes/GIstateToStr/GIstatestrToIndex) and their backing data moved verbatim from cli/utils.py, which is now a re-export shim (call sites unchanged). Adds a GIState IntEnum (0-based, matching the Solidity enum ordinals) and two validate_* predicates lifted from DinContext's GI-state guards, raising the SDK's ValidationError. The two context.py guards now delegate the decision to these predicates while keeping identical console output + exit codes — no CLI behavior change.
  • dincli/sdk/serialize.pyto_envelope() + a metadata-driven JSON encoder per proposal §3: the {status, data, error, meta} envelope (with schema_version/sdk_version) and the encoder rules — uint256_string → decimal string, omit, small-int passthrough, bytes/HexBytes0x hex, address → checksummed, enum → name, Decimal → string, None → null (except tagged omit).

Verification

  • Unit suite (SDK + CLI unit tests): 144 passed (incl. 23 new state/serialize tests: one per encoder rule + both envelope paths, converter snapshot + shim-identity + predicate checks).
  • Import-boundary test extended to assert state and serialize import clean of typer/rich/dincli.cli.* — green across all 11 SDK modules.
  • dincli --help + full-package import smoke pass through the shims (behavior parity).
  • Not run here (external toolchains, pre-existing, unaffected by this slice): the contract-integration suite (tests/dincli/*, needs the Hardhat/Foundry compile harness) and the Torch-dependent DP test (tests/test_cache_client_dp.py).

Scope vs #20 (explicitly partial)

  • ✅ inventory · ✅ JSON conventions defined + implemented (serialize.py) · ✅ SDK tests · ✅ interface/back-compat documented
  • ✅ shared-module extraction: config/log/web3/ipfs/contracts/manifest/runtime + state/serialize done
  • ⬜ CLI-commands-render-JSON refactor (operations/ role layer) — not started (depends on wallet.py/session.py)
  • ⏭️ Deferred to a dedicated next task — the wallet/session/tx keystone (wallet.py, session.py/DinSession, SignerProvider §2, tx.send() §5b). Split out deliberately: private-key/keystore/signing logic warrants its own focused review pass (flagged for @umeradl given the crypto sensitivity), not a shared slot with unrelated work. tx.send() also needs DinSession, which doesn't exist yet.

So this does not close #20; it's the extraction foundation the operations/ layer and the signing keystone build on.

Note on the diff

Base branch feat/din-sdk is behind develop, so the file diff includes the develop-sync commits (P3 design docs, upgradeable-proxy/ProxyAdmin work, task files). The SDK-specific commits are the docs(design) proposal, feat(sdk) seed + serializer, and the refactor(sdk) loader/state extraction commits.

umeradl and others added 11 commits July 8, 2026 21:05
Documentation/ now describes only what exists in code on develop
(not the live Sepolia deployment), split by audience:
- public/ — participant-facing: setup, cli-reference (was common.md),
  getting-started, manifest, services, roles/, workflows/, guides/
- technical/ — internal: contracts/ (per-contract refs), mechanisms/,
  services/, testing/, upgradable-contracts/, ARCHITECTURE.md
  (placeholder moved from Developer/, lands via P3-DOC1)

Developer/ holds forward-looking material:
- design/ (new) — MECHANISM_DESIGN.md, suggested-staking-mechanism.md
  (moved from Documentation/technical/mechanisms — it specifies planned
  behavior, not current), feasibility-report.md
- proposals/ (was tooling/) — proposals for tools not yet built
- issues/, tasks/, discussion/, rejected-ideas/ unchanged

Also:
- Rewrote both index READMEs with the placement rule and layout
- Added "where does a new doc go" + graduation rule (shipped designs get
  rewritten as current-state in Documentation/technical/, not moved)
- Fixed all repo-internal links to moved files (CLAUDE.md, README.md,
  Developer/*, dincli/docker/node/README.md) and repaired relative links
  inside moved files; fixed dead blob/main GitHub URLs to blob/develop
- Added DevNet 2.0 mechanism design doc (design/MECHANISM_DESIGN.md)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nnect-wallet fixes + OWS spike

Squash of three commits from the PR InfiniteZeroFoundation#16 review branch (634ce1e, 5cd1a9b,
6905808):

- list-accounts source tag: [..] was parsed by Rich as a markup tag and
  dropped; use (..) so imported/created/legacy renders. Docker node README:
  .session is a file, not a directory (review bugs InfiniteZeroFoundation#1, InfiniteZeroFoundation#6).
- connect-wallet: confirm before overwriting an existing named keystore
  (--yes/-y to bypass, mirrors send-eth), never encrypt a new keystore with
  a stale in-memory cached password, and parse .env once per unlock instead
  of per _get_password call (review items InfiniteZeroFoundation#2, InfiniteZeroFoundation#3, InfiniteZeroFoundation#5). Adds 7 tests.
- OWS delegation feasibility spike (Developer/discussion/
  ows-delegation-feasibility.md): hands-on verification that OWS signs an
  EVM tx without exposing the key; scoped-policy delegation remains
  unproven/follow-up. Corrects wallet-setup.md's broken OWS
  export-keystore flow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cherry-picked from PR InfiniteZeroFoundation#28, relocated from Documentation/public/guides/
to Developer/proposals/: this documents research into a storage
provider dincli does not yet integrate (no FOC/filecoin-pin adapter
exists in ipfs.py), so per Developer/README.md's placement rule it
belongs with forward-looking tooling material, not with docs of what
already exists on develop.

Replaces the Storacha guide (PR InfiniteZeroFoundation#26, closed) as the storage
recommendation for Discussion InfiniteZeroFoundation#18: Storacha's infrastructure (console,
API host, and both storacha.link/w3s.link retrieval gateways) is down
as of this research -- Cloudflare Error 1000 on the apex domain,
NXDOMAIN on console/API subdomains, gateways redirecting into the
broken root. Not a frontend-only issue.

Covers Filecoin Pin (the filecoin-pin CLI/library, built on FOC's
Synapse SDK) in the same three-part structure as the Storacha guide:
Web setup, CLI upload/retrieve, and authentication -- wallet keys and
the Session Key delegation model, which turns out to be a much closer
parallel to Storacha's UCAN delegation than raw-key-or-nothing.

Fact-checked against the filecoin-pin GitHub repo directly (README,
CLI source in src/commands/, documentation/retrieval.md and
content-routing-faq.md) rather than docs.filecoin.io, which 404'd on
the CLI walkthrough page during this research. Caught and corrected
one safety-critical error from an earlier research pass: the CLI
defaults to Mainnet (real funds), not Calibration testnet as
initially assumed.
Discussion InfiniteZeroFoundation#18 hands-on testing ruled out both providers this doc
recommended: Lighthouse retrieval is payment-gated (fails the
"readers pay nothing" requirement), and Storacha's infrastructure is
down/deprecated. Point at the new Filecoin Onchain Cloud proposal
(Developer/proposals/filecoin-onchain-cloud.md) instead of leaving a
stale recommendation for the next reader to walk into.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…n guide (relocated)

Manually merged PR InfiniteZeroFoundation#28 by Similoluwa
Abidoye. Content unchanged from the PR except relocation from
Documentation/public/guides/ to Developer/proposals/: FOC/filecoin-pin
is not integrated into dincli (no code changed here, ipfs.py still
only supports env/filebase/custom), so per Developer/README.md's
placement rule this is forward-looking research material, not
documentation of what exists on develop.

Also updates Developer/discussion/add-filecoin-support.md, which
previously recommended Lighthouse and Storacha as the best
Filecoin-backed providers -- both since ruled out by this same
research (Discussion InfiniteZeroFoundation#18) -- so it no longer contradicts the new
proposal doc.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
InfiniteZeroFoundation#20)

v0.3 proposal for the dincli/sdk/ extraction: five design principles, the
JSON envelope contract, DinError taxonomy with reserved tx/rpc subcodes,
non-interactive DinSession/SignerProvider, plan/apply (three-phase for
CID-producing ops) confirmation model, richer TxReceiptInfo, and backward-
compat guarantees. Candidate seed for P3-DOC7. Incorporates two rounds of
code-audit against real call sites.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…niteZeroFoundation#22)

Manually merges the Part 1 deliverable of task_060726_4 from
Similoluwa Abidoye (@Abidoyesimze, PR InfiniteZeroFoundation#22, branch security/foundry-src-review).

The 7 contracts in foundry/src/ are byte-identical between the audit's
pinned commit (d136ff3) and current develop, so the findings and line
references apply as-is to the live contracts.

- Documentation/technical/audits/foundry-src-security-review.md
  (relocated from the PR's Developer/audits/ path)
- foundry/test/SecurityFindings.t.sol — 9 PoC/verification tests, all
  passing (forge test --match-contract SecurityFindingsTest).

Findings: 0 Critical, 4 High, 4 Medium, 8 Low/Informational.
Open follow-ups tracked on PR InfiniteZeroFoundation#22: severity of H-1/H-2 (candidate for
Critical) and a measured spec-scale gas number for H-1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…h real forge measurements

Follow-up to PR InfiniteZeroFoundation#22 review feedback:

1. Severity: H-1 (Sybil-cheap gas-DoS) and H-2 (zero-CID sentinel
   collision) elevated to Critical (now C-1/C-2). Both permanently
   brick a Global Iteration on non-upgradeable contracts with no
   recovery path, and both are cheap (C-1) or free (C-2) for a single
   unprivileged participant to trigger. Remaining High findings
   renumbered H-1/H-2; no finding content changed, only labels.

2. Gas number: added test_gas_finalizeEvaluation_and_slashAuditors_atScale,
   measuring real forge gas at two registrant scales (10 and 30
   batches at demo params) and deriving the real marginal per-batch
   and per-(auditor,model)-pair cost from the slope between them.
   Params.auditorsPerBatch/modelsPerBatch has no setter, so literal
   spec-scale batch *internals* can't be deployed without modifying
   the contract (out of scope) -- the report is explicit that the
   final spec-scale projection layers a structural extrapolation on
   top of real measured data, not a second measurement, and that the
   slashAuditors number is a measured floor (its early-break behavior
   under an all-Sybil, zero-vote attack undermeasures the function's
   true worst case).

   Result: 223.5M gas (finalizeEvaluation) / 169.5M gas floor
   (slashAuditors) projected at full spec scale -- 5.6x-7.5x over
   Optimism's ~30M block limit, corroborating and quantifying the
   original estimate rather than changing its conclusion.

10/10 tests passing in SecurityFindingsTest. Reproduced the
maintainer's UpgradeValidation.t.sol FFI/npx failure independently in
a second environment -- confirmed not related to this report's PoCs.
…ary test (InfiniteZeroFoundation#20)

First clean-seed extraction for the SDK layer:
- dincli/sdk/cid.py: moved verbatim from services/cid_utils (zero cli deps);
  services/cid_utils.py kept as a back-compat re-export shim (13 importers).
- dincli/sdk/errors.py: DinError hierarchy + reserved tx/rpc subcodes +
  allowlist-oriented sanitize_details (proposal §4/§4a).
- tests/test_sdk_boundary.py: fresh-subprocess import-boundary test enforcing
  'sdk imports no typer/rich/dincli.cli', plus error/sanitizer unit tests.

runtime/ipfs/contracts deferred to the loaders step: they import cli.utils/log
at module load, so moving them now would fail the boundary test until
config/manifest/web3/log are extracted. Unit suite green (76 passed, 1 skipped).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…iteZeroFoundation#20)

Move the dependency-root loader cluster out of dincli/cli into dincli/sdk,
leaving the old paths as re-export shims:
- sdk/config.py: config/env/network/IPFS-config resolution + constants +
  IPFSConfig; console.print -> logger; logging.getLogger("dincli") directly
  (no sdk.log import -> no config<->log cycle).
- sdk/log.py: logger + quiet _read_log_level() bootstrap (no missing-config
  warning at import).
- sdk/web3.py: get_w3 (ConnectionError kept; # TODO(sdk-keystones)).
- cli/utils.py: re-exports placed above the wallet constants they feed;
  wallet/keystore/manifest/tx code stays.
- cli/log.py: re-exports logger + stdlib logging (dincli.cli.context needs both).
Tests: add test_sdk_config.py (17) + test_sdk_web3.py (2); migrate
test_connect_wallet's config-file patch to sdk.config.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n-seed (InfiniteZeroFoundation#20)

- sdk/ipfs.py: 3-provider upload/retrieve from services/ipfs; imports
  redirected to sdk.config/sdk.log/sdk.cid; progress prints -> logger;
  # TODO(sdk-keystones): IpfsError.
- sdk/contracts.py: ABIs + get_contract_instance; lazy get_w3 from sdk.web3.
- services/ipfs.py, cli/contract_utils.py: re-export shims.
- tests/test_ipfs_config.py: migrate config-file + ipfs-global patches to
  sdk.config / sdk.ipfs (re-export does not redirect a moved fn's globals).

The dincli/sdk package now holds a dependency-clean foundation
(cid, errors, config, log, web3, ipfs, contracts); the import-boundary test
enforces no typer/rich/dincli.cli imports across all of it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Santiagocetran Santiagocetran mentioned this pull request Jul 13, 2026
6 tasks
@umeradl umeradl added the Draft label Jul 13, 2026
Santiagocetran and others added 17 commits July 13, 2026 15:56
Target architecture for the full stack — DAO/platform/task contracts,
indexer, SDK, dincli/dind, IPFS layer, din-node/worker — with layered
Mermaid + ASCII diagrams, key flows, and P4 dependency order. Links the
live diagram on mermaidviewer.com and indexes the doc in Developer/README.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…proxies (PR InfiniteZeroFoundation#13)

Land the platform-upgradeable work from PR InfiniteZeroFoundation#13:
- DinCoordinator, DinToken, DinValidatorStake, DINModelRegistry converted
  to initializer-based transparent-proxy implementations
- V2 stub implementations under hardhat/contracts/upgrade/ with upgrade
  test suites under hardhat/test/
- deploy-platform.ts / upgrade-platform.ts scripts plus shared
  deploy/{constants,helpers,types}.ts and test/helpers/platform.ts
- MockSlasher test mock, hardhat-upgrades tooling deps, updated
  DinCoordinator/DinToken ABIs
- .gitignore: stop ignoring hardhat/scripts and hardhat/test so the
  deploy/upgrade tooling is tracked on develop

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…) with dincli harness, docs, wallet split

Follow-up integration on top of the PR InfiniteZeroFoundation#13 contract work:
- dincli test harness (tests/dincli/) updated to deploy the platform via
  the interim deploy-platform.ts script + import-deployments flow;
  hardhat/deployments/localhost.json and din_info.json tracked for it
- dincli: register-wallet/connect-wallet split follow-ups in
  cli/{context,core,system,utils}.py and main.py; regenerated
  DINModelRegistry/DinValidatorStake ABIs
- Documentation/technical/contracts/hardhat/: per-file docs for the new
  deploy/upgrade tooling, V2 contracts, and test suites (with coverage
  gaps); refreshed platform contract docs for the proxy conversion
- Documentation/technical/testing/: new docs for dintoken, ipfs-config,
  cache-client-dp, connect-wallet test suites; testing-guide and
  containerization-guide updates
- Documentation/public/: wallet-setup, keystore-migration, setup,
  getting-started, cli-reference, model-workflow updated for the wallet
  split and upgradeable deployment
- Developer/issues/dincli-native-proxy-deployment.md: backlog Option C
  (native dincli proxy deployment); retire technical/ARCHITECTURE.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… the loader tier (InfiniteZeroFoundation#20)

Move the manifest / din_info functions and ServiceRuntimeContext into
dincli/sdk, leaving the old paths as backward-compat shims:
- sdk/manifest.py: load_din_info, save_din_info, load_cid_services,
  download_manifest, get_model_info, get_manifest, get_manifest_path,
  get_manifest_key, is_ethereum_address; patchable DIN_INFO_PATH /
  CID_SERVICES_PATH constants; logging.getLogger("dincli") directly (no
  sdk.log import -> no cycle). Presentation stripped: cache_manifest's
  negative-id guard -> ValidationError; the info-block printing lifted out
  into a pure get_model_info returning data; the freshness warning ->
  logger.warning.
- sdk/runtime.py: ServiceRuntimeContext + build_service_runtime_context,
  import repointed to sdk.manifest.
- cli/utils.py: re-exports (above the wrapper); cache_manifest is now a thin
  CLI wrapper composing download_manifest + get_model_info + console.print and
  mapping ValidationError -> typer.Exit(1) (main.py has no DinError handler),
  reproducing the original info output verbatim.
- services/runtime.py: re-export shim.

Completes the SDK loader tier (cid, errors, config, log, web3, ipfs,
contracts, manifest, runtime). No sdk<->cli cycle; import-boundary test now
covers manifest+runtime. Tests: +22 (test_sdk_manifest), suite 117 passed /
1 skipped; behavior parity + tests/dincli import integrity verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/home/azureuser

constants.py now resolves everything from the repo checkout, home
directory, or environment: repo root via __file__, venvs via
~/my_venvs/<name>, npx via the newest ~/.nvm/versions/node/*/bin/npx,
scratch dir ~/tempdir/dincli. All resolutions are overridable via env
vars (PYDIN_PYTHON, TORCHENV_PYTHON, TORCHENV_SITE_PACKAGES, NPX_BIN,
IPFS_BIN, DIN_TEST_TMPDIR), read from the real environment first and
then the repo-root .env. conftest.py and test_04_gi.py consume the
shared constants instead of their own literals.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ences

Rewrite all 101 remaining /home/azureuser occurrences across 17 files
in Documentation/ and Developer/:

- markdown links to repo files become relative links (GitHub-navigable)
- shell/repo paths use the /path/to/devnet placeholder
- venv and scratch paths use ~/my_venvs/... and ~/tempdir/dincli/
- HR profile pointers in migrate_to_foundy.md use ~/projects/HR/...
- DIN_STATE_DIR example uses /home/<user>/.din-node; drop a stray
  duplicate mkdir line in containerization-guide.md

Also repoint references to the six per-model service files
(client/aggregator/auditor/model/scoring/modelowner.py) from
dincli/services/ to cache_model_0/services/ — they moved there when
services became per-model — and retarget the proposed contribution.py
module to cache_model_0/services/ for consistency.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Working summary of Abraham Nash's DIN white paper (Jun 2026) with a
gap/alignment checklist mapping paper mechanisms to DevNet status,
feeding the P3 mechanism-design push.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…acking into docs

- design/p3-design-plan.md: coordination page for the P3 mechanism-design
  push — mechanism -> design sources -> GitHub issue map (issues 36-43),
  open-decision -> discussion map (discussions 44-46), white-paper gap
  routing, definition of done
- ROADMAP.md: P3 status note (delayed pending designs; P4 active in
  parallel; dates not binding)
- MECHANISM_DESIGN.md, README.md: cross-references

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… (PR InfiniteZeroFoundation#35)

Original work by Robert (@robertocarlous, PR InfiniteZeroFoundation#35, branch
feature/foundry-platform-deployments, head 45dcda1), implementing issue
InfiniteZeroFoundation#34: DeployPlatform.s.sol, UpgradePlatform.s.sol, four V2 upgrade stubs
(DinTokenV2, DinCoordinatorV2, DinValidatorStakeV2, DINModelRegistryV2),
the forge test suite (DeployPlatform.t.sol), dincli's
`system import-deployments --foundry` flag, and the
foundry.toml/package.json/.gitignore changes needed to run them.

Three blocking bugs found in local verification, fixed here:
- DeployPlatform.s.sol: vm.writeJson has no parent dir on a fresh clone
  (foundry/deployments/ is gitignored and not committed) — call
  vm.createDir before vm.writeJson.
- All four V2 stubs: a plain `//` comment sat between the two `///`
  NatSpec lines, splitting the doc block so `@custom:oz-upgrades-from`
  never reached solc — UpgradePlatform.s.sol failed upgrades-core
  validation for every contract. Moved the `//` explanation above the
  NatSpec block so the `///` lines stay contiguous.
- Both scripts' own documented "Usage (from repo root)" command didn't
  compile (foundry.toml/remappings.txt live in foundry/, not repo
  root) — changed the usage comments to `cd foundry && forge script ...`.

Verified end-to-end: forge test (40/40 passing), and both scripts run
against a local chain (deploy -> deployments JSON -> dincli system
import-deployments --foundry -> upgrade, version() == 2 through the
proxy for all four contracts).

Co-Authored-By: umeradl <umermajeed.cto@gmail.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… harness

tests/dincli/ can now deploy the platform contracts via either the new
forge script from PR InfiniteZeroFoundation#35 or the existing hardhat script, selected by
PLATFORM_DEPLOY_TOOLCHAIN (tests/dincli/constants.py; default "foundry",
matching `dincli system import-deployments`'s own default). Overridable
via env var / .env for anyone who wants to keep exercising the hardhat
path.

- constants.py: FOUNDRY_DIR, FORGE_BIN, PLATFORM_DEPLOY_TOOLCHAIN,
  HARDHAT_DEV_ACCOUNT_0; DEPLOYMENTS_FILE now resolves under whichever
  toolchain's deployments/ dir.
- test_01_platform.py: test_deploy_platform_via_script branches on the
  toolchain (forge script vs hardhat run); test_import_deployments_into_din_info
  keeps passing an explicit --file since import-deployments' own
  --foundry/--hardhat default-path resolution is relative to cwd, and
  the harness's cwd is the isolated DIN_TEMP scratch dir, not the repo
  root.
- conftest.py: managed_services now starts the matching chain backend
  (Anvil via foundry/anvil.sh for "foundry", the existing Hardhat node
  for "hardhat" — both on chain-id 1337) and runs `forge build` when
  the foundry toolchain is active; `npx hardhat compile` still always
  runs since task-level contract deploys and dump-abi tests need
  hardhat's ABIs/bytecode regardless of which toolchain deployed the
  platform.

Verified: full tests/dincli/test_01_platform.py (8/8) under both
toolchains, and test_01 through test_03 (35/35) end-to-end under the
new foundry default.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two follow-up fixes to files landed in b40fe2e (PR InfiniteZeroFoundation#35 foundry
platform deploy/upgrade parity):

- foundry/test/DeployPlatform.t.sol: the DinCoordinatorV2/
  DinValidatorStakeV2/DINModelRegistryV2 imports sat mid-file, after
  the MockTaskContract definition — moved them up next to the
  DinTokenV2 import at the top, with the rest of the imports.
- foundry/script/DeployPlatform.s.sol: added a `forge clean` step to
  the documented "Usage (from repo root)" comment, right after
  starting anvil. Without it, deploying then testing back-to-back
  after a recompile can hit upgrades-core's stale build-info bug
  ("Found multiple contracts with name ...") — reproduced again while
  re-verifying this change; `forge clean` resolves it.

Also ran `forge fmt` over both files (line-wrapping only, no logic
changes).

Re-verified: forge clean && forge test — 40/40 passing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds staking-design.md and tokenomics-design.md, and updates the
mechanism design, staking mechanism, and P3 plan docs to reflect them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ions

Records outcomes from Abraham's Slack sync (2026-07-21) against the open
decisions tracked in MECHANISM_DESIGN §9, staking-design.md §5, and
tokenomics-design.md §6: slashed-stake destination (50/50 burn/treasury),
depositAndMint cap/retirement, emission decay with MAX_SUPPLY left open,
dual-source burn policy, delegation/tombstoning scope, and the white-paper
InfiniteZeroFoundation#46 alignment items. Fee denomination is resolved as a DIN/ETH split
(protocol fees in DIN, validator network fees in ETH), not the DIN-only
recommendation these docs previously carried.

GitHub Discussions InfiniteZeroFoundation#44/InfiniteZeroFoundation#45/InfiniteZeroFoundation#46 themselves are not yet updated to reflect
this — noted in p3-design-plan.md so the "resolved" checkbox isn't marked
done prematurely.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…iteZeroFoundation#37/InfiniteZeroFoundation#42/InfiniteZeroFoundation#43)

Scopes a 5-day platform-contract task combining the now-resolved staking,
tokenomics, and fees/treasury designs: new DinTreasury and DinFeeRouter
contracts, DinToken.burn(), DinCoordinator mint-cap/faucet-retirement/
treasury-routing, DinValidatorStake governable params + real jailing, and
a DIN-denominated fee path alongside DINModelRegistry's existing ETH fees.
Includes the exact deployment/wiring order and test plan.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nfiniteZeroFoundation#39/InfiniteZeroFoundation#40/InfiniteZeroFoundation#41/InfiniteZeroFoundation#38)

Adds task_210726_6 (scoring validation, commit-reveal auditing, encrypted
test-data distribution, per-GI reward engine bootstrap) plus a prior-work
reference doc mapping BlockFlow/2CP/Shapley-value-data-valuation/IPFS onto
the task's scoring and auditing mechanisms.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…capabilities task (issues InfiniteZeroFoundation#20/InfiniteZeroFoundation#21)

Co-Authored-By: umeradl <umermajeed.cto@gmail.com>
robertocarlous and others added 8 commits July 22, 2026 15:06
OZ v5 TransparentUpgradeableProxy deploys one ProxyAdmin per proxy.
The previous script read the admin from the token proxy only and wrote
a single proxyAdmin key, leaving the other three addresses unrecorded.

Now reads getAdminAddress for each of the four proxies and writes
proxyAdminToken, proxyAdminCoordinator, proxyAdminStake, and
proxyAdminRegistry into foundry/deployments/<network>.json.
Same fix as the Foundry script: read the ProxyAdmin address for each
proxy individually and write proxyAdminToken, proxyAdminCoordinator,
proxyAdminStake, and proxyAdminRegistry into the deployments JSON.
PlatformAddresses type updated to match.
Update _DEPLOYMENTS_TO_DIN_INFO to map the four proxyAdmin* keys
(proxyAdminToken, proxyAdminCoordinator, proxyAdminStake,
proxyAdminRegistry) to their corresponding proxy_admin_* keys in
din_info.json, replacing the single proxyAdmin → proxy_admin mapping.
…proxy-admin-addresses

Fix/proxy admin addresses
…severity-and-gas-followups

audit(foundry): elevate H-1/H-2 to Critical, add real gas measurements
# Conflicts:
#	tests/test_connect_wallet.py
… validate predicates (InfiniteZeroFoundation#20)

Move GIstateToDes/GIstateToStr/GIstatestrToIndex and their backing data
(states, stateDescription, GIstate_to_index) verbatim from cli/utils.py into a
pure dincli/sdk/state.py; utils.py now re-exports them as a shim (same pattern as
the ipfs/runtime waves) so existing call sites keep working unchanged.

Add a GIState IntEnum (0-based, matching the Solidity enum ordinals) and two
validate_* predicates lifted from DinContext's GI-state guards
(validate_gi_state_equals / validate_gi_state_at_least), raising the SDK's
ValidationError instead of printing + typer.Exit. The two context.py guards now
delegate the decision to these predicates while keeping identical console output
and exit codes (no CLI behavior change).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eroFoundation#20)

Implement to_envelope() + a metadata-driven JSON encoder per proposal §3: the
{status, data, error, meta} envelope with schema_version/sdk_version, and the
encoder rules — uint256_string -> decimal string, omit, small-int passthrough,
bytes/HexBytes -> 0x hex, address -> checksummed, enum -> name, Decimal -> string,
and None -> null (except tagged omit). Extend the import-boundary test to assert
state and serialize import clean of typer/rich/dincli.cli.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Santiagocetran Santiagocetran changed the title SDK #20 (draft): dincli/sdk foundation — cid/errors + config/log/web3 + ipfs/contracts SDK #20 (draft): dincli/sdk foundation — loaders + manifest/runtime + state/serialize Jul 23, 2026
@umeradl umeradl added this to the Devnet 3.0 P4 milestone Jul 23, 2026

@umeradl umeradl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review — dincli/sdk foundation (loaders + manifest/runtime + state/serialize)

Reviewed at commit 6eac109 (feat/din-sdk, contributor Santiagocetran's fork), worktree /home/umerm/tempdir/DIN/DraftPRs/pr-31, diffed against origin/develop at 45cbfad. Scope: issue #20. Everything below was reproduced directly — actual diffs read line-by-line, actual test runs, actual git-blame comparisons against the pre-move code — not taken from the PR body.

git diff origin/develop...HEAD --stat: 28 files changed, 2682 insertions(+), 1046 deletions(-).

Files changed — PR #31 unique, at HEAD commit 6eac109 (name-status)

A	dincli/sdk/__init__.py
A	dincli/sdk/cid.py
A	dincli/sdk/ipfs.py
A	Developer/design/sdk-interface-proposal.md
M	dincli/cli/context.py
M	dincli/cli/contract_utils.py
A	dincli/sdk/contracts.py
M	dincli/cli/log.py
A	dincli/sdk/log.py
M	dincli/cli/utils.py
M	dincli/services/cid_utils.py
M	dincli/services/ipfs.py
A	dincli/sdk/config.py
A	dincli/sdk/errors.py
A	dincli/sdk/manifest.py
A	dincli/sdk/runtime.py
A	dincli/sdk/serialize.py
A	dincli/sdk/state.py
A	dincli/sdk/web3.py
M	dincli/services/runtime.py
M	tests/test_connect_wallet.py
M	tests/test_ipfs_config.py
A	tests/test_sdk_boundary.py
A	tests/test_sdk_config.py
A	tests/test_sdk_manifest.py
A	tests/test_sdk_serialize.py
A	tests/test_sdk_state.py
A	tests/test_sdk_web3.py

What's good

  • state.py GI-state converters are byte-identical to the pre-move code. Diffed dincli/sdk/state.py's stateDescription/states/GIstateToDes/GIstateToStr/GIstatestrToIndex against git show origin/develop:dincli/cli/utils.py (lines 565-642) — only whitespace differs. Genuinely verbatim, as claimed.
  • The new GIState IntEnum's ordinals match Solidity exactly. Checked foundry/src/DINShared.sol's enum GIstates { AwaitingDINTaskAuditorToBeSet, // 0 ... GIended // 22 } (23 members, 0-22) against the states list in state.py — same 23 names, same order, index-for-index. hardhat/contracts/DINShared.sol matches too (both toolchains use the identical enum).
  • The context.py delegation preserves exit-code/message semantics. validate_GIstate_ET_given_GIstate and validate_GIstate_LTE_given_GIstate now call validate_gi_state_equals/validate_gi_state_at_least and catch ValidationError, but the console.print(f"[bold red]✗ {msg}. Current state: ...[/bold red]") + raise typer.Exit(1) fallback is untouched and fires on the same conditions as the pre-extraction inline checks. Traced both sides; logic is equivalent.
  • validation_failed's detail allowlist matches what state.py actually raises. _ALLOWLIST["validation_failed"] = {"field": "str", "expected": "str", "actual": "str"} in errors.py lines up exactly with the details={"field": ..., "expected": ..., "actual": ...} dict both validate_gi_state_equals and validate_gi_state_at_least construct — nothing gets silently dropped by sanitize_details.
  • serialize.py's encoder rules all work correctly on the edge cases I tried by hand (not just the happy-path tests already in the file): nested dataclasses, a list of dataclasses, uint256 at 2**256-1 (Python's arbitrary-precision int means no truncation regardless of value size), zero, and tagged-None fields all serialize correctly. See finding No. 3 for the one edge case that isn't clean.
  • The import-boundary test is real and actually covers the new modules, not just the old ones. tests/test_sdk_boundary.py::test_sdk_imports_no_cli_or_ui runs in a fresh subprocess, walks every submodule under dincli.sdk via pkgutil.walk_packages, and explicitly asserts {"dincli.sdk.state", "dincli.sdk.serialize"} <= loaded before checking neither typer/rich nor any dincli.cli.* module got pulled in. This is exactly the test Umer asked for, and it's not vacuous — I ran it standalone and it passes.
  • Full test suite passes: 144 passed (torch-gated tests/test_cache_client_dp.py and the live-chain tests/dincli/ directory excluded per the task brief — both need dependencies not present in this venv). This matches the PR body's claimed count. The SDK-focused subset (test_sdk_state.py, test_sdk_serialize.py, test_sdk_boundary.py, test_sdk_manifest.py, test_sdk_config.py, test_sdk_web3.py) is 70 of those 144 and all pass on their own.
  • Two of the three loader-tier shims I spot-checked in depth (dincli/services/runtime.py, dincli/services/cid_utils.py) are genuinely behavior-identical — diffed them directly against dincli/sdk/runtime.py/dincli/sdk/cid.py and the only differences are import paths and a docstring header.

Findings

No. 1 — systemic, repeated departure from the "zero behavior change" claim: every console.print in code that moved into dincli/sdk/* silently became a logger.* call. Non-blocking, but should be called out explicitly rather than left implied by "zero behavior change." ----- ** No Action Needed Santigo - Just flagging what behaviour changed **

This isn't a one-off — it's the same pattern in at least three places across the loader-tier commits:

  1. dincli/sdk/ipfs.py::upload_to_ipfs — the three provider-branch status lines that were console.print("[bold green]Uploading via Environment-backed IPFS...[/bold green]") (and the Filebase/custom equivalents) in the pre-move dincli/services/ipfs.py are now logger.info("Uploading via Environment-backed IPFS..."). Confirmed by diffing git show origin/develop:dincli/services/ipfs.py against dincli/sdk/ipfs.py — every other message in that file already used logger.* before the move; only these three were rich-styled console.print calls, and only these three changed character.
  2. dincli/sdk/manifest.py::get_manifest — the freshness-check warning, previously console.print(f"[yellow]Warning: Could not verify manifest freshness: {e}[/yellow]") in cache_manifest's ancestor code (git show origin/develop:dincli/cli/utils.py around line 750), is now logger.warning("Could not verify manifest freshness: %s", e).
  3. dincli/sdk/config.py::get_env_key/set_env_key — the "{key} not found in .env file" (was [bold red]), "python-dotenv not installed" (was [yellow]), and "Error saving to .env" (was [red]) messages are all now logger.warning.

None of these are functional regressions — dincli/sdk genuinely cannot import rich per the architectural constraint this whole PR exists to establish, so some conversion was unavoidable, and the CLI-facing wrapper cache_manifest in cli/utils.py still does its own console.print for the messages that matter for exit-code semantics (verified — see "What's good" above). But three concrete, user-visible things actually changed for these specific lines: they lose rich styling/color, they move from stdout to stderr (Python logging's default StreamHandler target), and they become gate-able by the user's configured log_level — previously these particular console.print calls fired unconditionally regardless of log-level config, so a user who has set log_level: WARNING or higher in their dincli config would now silently lose the "Uploading via..." status lines (INFO-level) that used to always print. At the default log_level: INFO all three still surface, just reformatted.

This is exactly the kind of thing the "zero behavior change, all call sites unchanged" claim in the loader-tier commit messages should not paper over. Recommend either amending those commit messages/PR description to scope the claim to "unchanged call sites and return values" (true) rather than "unchanged behavior" (not quite true for these log/console lines), or explicitly listing this console→logger conversion as a known, intentional side effect of the SDK boundary.

No. 2 — cache_manifest(..., info=True) now makes two on-chain RPC calls (getModel) where the old code made one. Non-blocking (minor inefficiency, not a correctness bug), but worth flagging since it's a real behavior change the "zero behavior change" claim doesn't account for.

Old cache_manifest (single function) fetched model_info = din_registry_contract.functions.getModel(model_id).call() once and used it both for the info print branch and the download/freshness check. The new split (dincli/sdk/manifest.py::download_manifest + get_model_info, glued back together by cli/utils.py::cache_manifest) calls download_manifest() (which may itself call getModel if the manifest is missing/stale) and then, independently, get_model_info() calls getModel again for the info dict. tests/test_sdk_manifest.py::TestCacheManifestCLIWrapper::test_info_flag_calls_get_model_info_and_prints actually asserts this double-call shape (download_calls == [5] and get_model_info_calls == [(5, True)] as two separate mock invocations), so it's tested — just not flagged as a divergence from the original single-RPC-call design. Any dincli ... --info command now does one extra eth_call round-trip. Low severity (RPC calls are cheap and idempotent reads), but real, and it's the kind of thing worth a one-line callout in the PR description rather than leaving under a "zero behavior change" umbrella.

No. 3 — serialize.py's uint256_string and address encoders don't validate their input; a negative int or a malformed address propagates either an unvalidated negative string or a raw ValueError (not a DinError) out of the SDK. Non-blocking follow-up, not a blocker for this PR.

Reproduced both by hand:

>>> _encode_dataclass(Outer(inner=Inner(val=5), items=[...], negative=-42, huge=2**256-1))
{'inner': {'val': '5'}, ..., 'negative': '-42', 'huge': '115792089237316195423570985008687907853269984665640564039457584007913129639935'}

str(int(raw)) happily stringifies a negative number tagged uint256_string — there's no assertion that the value is actually in [0, 2**256). Given these values are expected to always originate from on-chain uint256 reads, this is unlikely to trigger in practice, but if it ever does (a bug upstream, a manually-constructed dataclass in a future daemon handler), the envelope would carry a semantically-invalid "-42" for a field the wire-protocol schema promises is a non-negative uint256 string, with no error raised anywhere.

>>> _encode_dataclass(Addr(a='not_an_address'))
ValueError: when sending a str, it must be a hex string. Got: 'not_an_address'

A malformed address-tagged field raises a bare ValueError from Web3.to_checksum_address, not one of the DinError subclasses this same PR's errors.py establishes as the required shape for anything that crosses the SDK boundary. If to_envelope() is ever called with a dataclass containing a bad address (e.g. from a future daemon handler that didn't validate input upstream), the raw ValueError would propagate uncaught rather than becoming a structured {"code": ..., "message": ..., "details": ...} error — undermining the "every SDK-boundary error should be JSON-shaped" property the design review approved in §4.

Neither of these is exercised by tests/test_sdk_serialize.py — that file is happy-path only for these two encoders (valid, non-negative, well-formed inputs). Test coverage is otherwise decent (uint256 stringification, omit, checksum, enum-to-name, Decimal, nullable-tagged-None, and envelope success/error/null-data/correlation_id are all covered), but these two negative-input paths aren't.

No. 4 — two open asks from the design review (already flagged there, confirmed still unaddressed by this diff) — non-blocking follow-ups, not blockers for this PR.

  • Amount-field tagging scan test: the design review (review.md in this folder, §2 close-out) asked for "a test that scans dataclasses for amount-shaped int fields and asserts they're tagged [metadata={"json": "uint256_string"}], same allowlist discipline §4a already applies to error details." No such test exists anywhere in this diff (grep -rln "uint256_string" across the tree hits only dincli/sdk/serialize.py itself and its own test file — no scanner). Still open; reasonable to defer to whenever the first real daemon-facing dataclasses land (this PR only ships the encoder + test fixtures, no production dataclasses to scan yet), but worth a tracking note so it doesn't quietly fall off.
  • correlation_id origin: the design review asked for a one-line clarification of where correlation_id comes from (generated per-call? tied to a job id?). Developer/design/sdk-interface-proposal.md is unchanged on this point since that review (still just "optional for CLI, first-class for daemon jobs/logs" with no origin spec), and to_envelope()'s implementation just accepts whatever string the caller passes — it doesn't generate or attach one itself. Still open, still non-blocking (this is presumably PR #32's / the daemon's job to decide).

No. 5 — dincli/sdk/ipfs.py's # TODO(sdk-keystones): IpfsError is mislabeled: it isn't actually blocked on the deferred wallet/session/tx keystone, and it's a real gap for dind's retry logic today. Non-blocking, but should be re-scoped/actioned separately rather than left bundled with the private-key-sensitive work.

_raise_for_http_error (ipfs.py:89-98) and the requests.exceptions.RequestException catches in upload_to_ipfs/retrieve_from_ipfs (ipfs.py:183-184, 252-254) all raise a bare RuntimeError, tagged with a TODO(sdk-keystones): IpfsError comment implying the fix is gated on the same keystone work as wallet.py/session.py/tx.py (dincli/sdk/web3.py:14 has the equivalent TODO(sdk-keystones): NetworkError). But IpfsError already exists and is fully wired into the error taxonomy this same PR ships: errors.py:70 (class IpfsError(DinError): code = "ipfs_error"), and Developer/design/sdk-interface-proposal.md:281 explicitly calls out converting these raw RuntimeErrors into it. There's no dependency on DinSession/SignerProvider here — IpfsError doesn't touch signing at all, it just needs raise IpfsError(...) swapped in for raise RuntimeError(...) in the three spots above (plus an _ALLOWLIST entry in errors.py, e.g. {"provider": "str", "status_code": "int"}, so the detail sanitization the rest of errors.py enforces actually covers it).

This matters concretely for dind (PR #32): per errors.py's own module docstring, "the daemon maps [DinErrors] to job errors and keys retries off the stable code." A failed IPFS upload today raises a RuntimeError with no .codedind's future job-retry policy has nothing structured to key off for "IPFS upload failed, should this job retry?" vs. any other unexpected exception. Recommend either wiring IpfsError now (it's a small, low-risk change fully within this PR's own architecture) or re-tagging the TODO so it doesn't read as blocked on the keystone task it isn't actually blocked on.

No. 6 — to_envelope() (§3's JSON envelope) has zero production call sites: nothing in either PR actually wires an SDK operation's dataclass return through it yet. Expected sequencing gap, not a regression — flagging so it's tracked explicitly rather than left implicit.

grep -rn "to_envelope" --include="*.py" across both worktrees turns up exactly two hits: the function's own definition in dincli/sdk/serialize.py and its test file, tests/test_sdk_serialize.py. No production code — not dincli/cli/*, not dincli/dind/* (PR #32) — ever calls it. Consistent with that: dincli/dind/jobs.py's JOB_HANDLERS registry has exactly one entry, "demo", and it's a no-op; and there is no --json flag anywhere in dincli/cli/ (grepped, no hits) for the CLI half of §3's "daemon always; CLI on --json" consumer-boundary design. So while to_envelope() itself is correctly built and tested (see "What's good" and No. 3 above), the pipeline that's supposed to produce the dataclasses it consumes — the sdk/operations/ layer from §8's package layout, calling real SDK functions for task registration, GI lifecycle, aggregator/auditor scoring, etc. — doesn't exist in either PR yet.

This is expected, not a bug: operations/ is explicitly one of the modules gated behind the deferred wallet/session/tx keystone (§8, "the remaining seven"), so there's nothing for it to wrap yet. Once that keystone lands and operations/ starts shipping real functions, dind will register real job handlers (client/auditor/aggregator role automation) that call into operations/ and route the resulting dataclasses through to_envelope() for the job queue and /health/logs — replacing today's single no-op "demo" handler. Recommend tracking this explicitly — added as BL-9 in Developer/BACK_LOG.md alongside this review — so "envelope built, nothing consumes it yet" doesn't quietly get lost as an implicit gap between the two PRs.

Recommendation

Mergeable as-is, modulo the deferred wallet/session/tx keystone (explicitly out of scope for this PR). Nothing here is a functional regression that breaks a call site — the four loader-tier shims I checked (cli/utils.py, cli/contract_utils.py, services/ipfs.py, services/runtime.py, services/cid_utils.py) all correctly re-export working code, state.py is verified byte-identical and ordinal-correct against the Solidity enum, and the full suite (144 tests) passes clean.

The one thing I'd push back on before merge is the wording of the commit messages / PR description: "zero behavior change" is not quite accurate for the loader-tier commits (No. 1, No. 2 above are real, if minor, behavior changes) — I'd ask for that language to be softened to something like "unchanged call sites and return contracts; console output for moved SDK-layer functions now routes through logging" so nobody downstream is surprised later. Findings No. 3 and No. 4 are legitimate gaps but genuinely low-stakes follow-up work, not blockers. No. 5 (the IpfsError TODO) is worth a quick fix or at least a re-scope before this lands, since it's cheap, self-contained, and directly feeds dind's retry logic — no reason to let it sit mislabeled as blocked on the keystone task. No. 6 (the unwired envelope/operations/ layer) isn't a blocker either — it's the natural next layer once the keystone lands, tracked in the backlog rather than left implicit.

umeradl added a commit that referenced this pull request Jul 30, 2026
…ne (issue #20 continued)

Next task after task_220726_7 (both parts landed clean, PR #31 @ 6eac109, PR
#32 @ 99f060b). Scopes DinSession/SignerProvider (§2) + wallet.py + tx.py's
send() keystone (§5b) per the approved SDK interface proposal, folding in
the nonce-management gap (BL-1) and the three other open §5b questions.
sdk/operations/ stays explicitly out of scope, same as this task was
deferred out of task_220726_7.

Co-Authored-By: umeradl <umermajeed.cto@gmail.com>
umeradl added a commit that referenced this pull request Jul 30, 2026
to_envelope() (PR #31) has zero production call sites and dind's
JOB_HANDLERS registry (PR #32) has only a no-op "demo" entry. Surfaced in
the PR #31/#32 code review (finding No. 6) as an expected sequencing gap,
gated behind the wallet/session/tx keystone (BL-2).

Co-Authored-By: umeradl <umermajeed.cto@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants