From 1233b42ad9b45ff5f53eec78c3bc23fba13e879a Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 07:11:16 +0200 Subject: [PATCH 01/47] Update to CMTAT v3.3.0-rc1 and other related librares, implement ERC-1643 rework --- .gitmodules | 3 + AGENTS.md | 88 ++++++ CHANGELOG.md | 64 +++- CLAUDE.md | 88 ++++++ README.md | 78 ++++- doc/ERCSpecification/ERC-1643-proposition.md | 129 ++++++++ doc/ERCSpecification/erc-1643.md | 158 ++++++++++ doc/slither-report.md | 38 --- foundry.lock | 20 ++ foundry.toml | 4 +- lib/CMTAT | 2 +- lib/RuleEngine | 1 + lib/forge-std | 2 +- lib/openzeppelin-contracts | 2 +- lib/openzeppelin-contracts-upgradeable | 2 +- remappings.txt | 1 + src/DocumentEngine.sol | 66 +++- src/DocumentEngineInvariant.sol | 35 ++- test/DocumentEngine.t.sol | 313 +++++++++++-------- 19 files changed, 877 insertions(+), 217 deletions(-) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 doc/ERCSpecification/ERC-1643-proposition.md create mode 100644 doc/ERCSpecification/erc-1643.md delete mode 100644 doc/slither-report.md create mode 100644 foundry.lock create mode 160000 lib/RuleEngine diff --git a/.gitmodules b/.gitmodules index 415e443..4d17ab7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "lib/openzeppelin-contracts-upgradeable"] path = lib/openzeppelin-contracts-upgradeable url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable +[submodule "lib/RuleEngine"] + path = lib/RuleEngine + url = https://github.com/CMTA/RuleEngine diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..ca68820 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,88 @@ +# DocumentEngine — Agent Guide + +> **Note — keep in sync:** `AGENTS.md` and `CLAUDE.md` must always be **identical**. +> Any edit to one must be applied verbatim to the other. + +> **Note — commit messages:** After each group of modifications or each feature +> added, always provide a **one-line GitHub commit message** (Conventional-Commits +> style, e.g. `feat: add token binding`, `fix: correct event args`, `docs: update README`). + +## What this project is + +`DocumentEngine` is a standalone smart contract that manages documents on-chain +through **ERC-1643** on behalf of **several** other smart contracts (e.g. CMTAT +tokens). Using an external engine keeps each token small and lets one operator +manage documents for a whole fleet of tokens. + +A document is `{ string uri, bytes32 documentHash, uint256 lastModified }`, +addressed by a `bytes32` name. + +## Key concepts + +- **Two management paths (both active at once):** + - **Admin path** — `DOCUMENT_MANAGER_ROLE`. Address-scoped overloads + (`setDocument(address,...)`, `removeDocument(address,...)`, batch variants) + manage documents for any contract. + - **Bound-token path** — `TOKEN_CONTRACT_ROLE` (the CMTA RuleEngine binding + pattern). The standard single-arg `IERC1643` functions (`setDocument(name,uri,hash)`, + `removeDocument(name)`) let a bound token manage its **own** namespace + (`_msgSender()`). Bind a token with `grantRole(TOKEN_CONTRACT_ROLE, token)`. +- **Events:** every write emits the standard `IERC1643` events **and** the optional + `DocumentUpdatedForContract` / `DocumentRemovedForContract` events (which add the + `smartContract` address). See `ERC-1643-proposition.md`. +- **ERC-2771:** meta-transaction (gasless) support; `_msgSender()` is used everywhere. +- **Access control:** `DEFAULT_ADMIN_ROLE` implicitly has every role (see the + `hasRole` override). +- **CMTAT integration:** since CMTAT v3, a token uses the engine via CMTAT's + `DocumentEngineModule` and `setDocumentEngine(engine)` (reads/writes are forwarded + keyed by the token address). Standard CMTAT standalone tokens store documents + on-chain instead and do **not** use this engine. + +## File tree + +``` +src/ +├── DocumentEngine.sol # Main contract: ERC-1643 impl, both management +│ # paths, batch functions, ERC-2771, access control +└── DocumentEngineInvariant.sol # Errors, roles (DOCUMENT_MANAGER_ROLE, + # TOKEN_CONTRACT_ROLE) and the optional multi-token events + +test/ +└── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin path, + # bound-token path, batch ops, CMTAT integration + # (CMTATDocumentEngineMock built on DocumentEngineModule) +``` + +Other important files: + +- `foundry.toml` — solc `0.8.34`, `evm_version = prague` (required by CMTAT v3). +- `remappings.txt` — `CMTAT/`, `RuleEngine/`, `OZ/`, `@openzeppelin/contracts-upgradeable/`. +- `CHANGELOG.md` — semver history; update on every release (current: `v0.4.0`). +- `ERC-1643-proposition.md` — proposed optional multi-token events / extension. +- `README.md` — full documentation and Surya schema. +- `doc/` — Surya diagrams/reports (`doc/script/`), Slither report, coverage. +- `lib/` — submodules: `CMTAT`, `RuleEngine`, `openzeppelin-contracts(-upgradeable)`, `forge-std`. + +## Dependencies (tested versions) + +- CMTAT `v3.3.0-rc1`, RuleEngine `v2.1.0` +- OpenZeppelin Contracts / Contracts Upgradeable `v5.6.1` +- Solidity `0.8.34`, Foundry + +## Common commands + +```bash +forge build # compile +forge test # run the test suite +forge fmt # format +forge test --gas-report +``` + +## Conventions + +- The contract `VERSION` constant (in `src/DocumentEngine.sol`) must match the + latest `CHANGELOG.md` entry on release. +- Bump `MAJOR` on incompatible proxy-storage / external-library or API changes, + `MINOR` for backward-compatible features, `PATCH` for backward-compatible fixes. +- A bound token can only ever affect its **own** document namespace — never break + that isolation. diff --git a/CHANGELOG.md b/CHANGELOG.md index 199d877..576d56c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,68 @@ # CHANGELOG -Please follow https://changelog.md/ conventions. +Please follow [https://changelog.md](https://changelog.md) conventions and the other conventions below + +## Semantic Version 2.0.0 + +Given a version number MAJOR.MINOR.PATCH, increment the: + +1. MAJOR version when the new version makes: + - Incompatible proxy **storage** change internally or through the upgrade of an external library (OpenZeppelin) + - A significant change in external APIs (public/external functions) or in the internal architecture +2. MINOR version when the new version adds functionality in a backward compatible manner +3. PATCH version when the new version makes backward compatible bug fixes + +See [https://semver.org](https://semver.org) + +## Type of changes + +- `Added` for new features. +- `Changed` for changes in existing functionality. +- `Deprecated` for soon-to-be removed features. +- `Removed` for now removed features. +- `Fixed` for any bug fixes. +- `Security` in case of vulnerabilities. + +Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) + +## Checklist + +> Before a new release, perform the following tasks + +- Code: Update the version name in the `Version` core module, variable VERSION +- Run linter + +> npm run-script lint:all:prettier + +- Documentation + - Perform a code coverage and update the files in the corresponding directory [./doc/general/test/coverage](./doc/general/test/coverage) + - Perform an audit with several audit tools (Aderyn and Slither), update the report in the corresponding directory [./doc/audits/tools](./doc/audits/tools) + - Update surya doc by running the 3 scripts in [./doc/script](./doc/script) + + - Update changelog + +## v0.4.0 + +### Changed + +- **Dependencies** + - Upgrade CMTAT `v2.5.0-rc0` → `v3.3.0-rc1` + - Upgrade OpenZeppelin Contracts (and Contracts Upgradeable) `v5.0.2` → `v5.6.1` + - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) `v2.1.0` as a submodule (binding-role reference) +- **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) +- **`IERC1643` (CMTAT v3) breaking changes** + - `getDocument(bytes32)` now returns a `Document` struct instead of the `(string, bytes32, uint256)` tuple. Both `getDocument` overloads updated accordingly. + - The `Document` struct and the `DocumentUpdated`/`DocumentRemoved` events are now provided by `IERC1643`; the duplicate local declarations were removed from `DocumentEngineInvariant`. + - Import path moved: `CMTAT/interfaces/engine/draft-IERC1643.sol` → `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. + +### Added + +- **Bound-token document management (RuleEngine binding pattern)**: implement the now-mandatory `IERC1643.setDocument(name, uri, hash)` and `removeDocument(name)`. They are gated by a new `TOKEN_CONTRACT_ROLE` and scoped to the caller (`_msgSender()`) own namespace. A token bound with `grantRole(TOKEN_CONTRACT_ROLE, token)` manages its own documents and can never affect another contract's documents. The existing admin overloads (explicit `address`, `DOCUMENT_MANAGER_ROLE`) are unchanged, so both systems work side by side. +- **Optional multi-token events**: alongside the standard `IERC1643` events, the engine now also emits `DocumentUpdatedForContract` / `DocumentRemovedForContract`, which carry the `smartContract` (token) address so off-chain indexers can tell which contract a document belongs to during multi-contract operations. See [`ERC-1643-proposition.md`](./ERC-1643-proposition.md) for the proposed optional standard extension. + +### Notes / bottlenecks + +- CMTAT v3 no longer ships a *standalone* token that consumes an external document engine through its constructor; the standard token stores documents on-chain (`DocumentERC1643Module`). External-engine integration now goes through CMTAT's `DocumentEngineModule` (`setDocumentEngine`). The test suite was updated to exercise this real integration path via a minimal token built on `DocumentEngineModule`. ## v0.3.0 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..ca68820 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,88 @@ +# DocumentEngine — Agent Guide + +> **Note — keep in sync:** `AGENTS.md` and `CLAUDE.md` must always be **identical**. +> Any edit to one must be applied verbatim to the other. + +> **Note — commit messages:** After each group of modifications or each feature +> added, always provide a **one-line GitHub commit message** (Conventional-Commits +> style, e.g. `feat: add token binding`, `fix: correct event args`, `docs: update README`). + +## What this project is + +`DocumentEngine` is a standalone smart contract that manages documents on-chain +through **ERC-1643** on behalf of **several** other smart contracts (e.g. CMTAT +tokens). Using an external engine keeps each token small and lets one operator +manage documents for a whole fleet of tokens. + +A document is `{ string uri, bytes32 documentHash, uint256 lastModified }`, +addressed by a `bytes32` name. + +## Key concepts + +- **Two management paths (both active at once):** + - **Admin path** — `DOCUMENT_MANAGER_ROLE`. Address-scoped overloads + (`setDocument(address,...)`, `removeDocument(address,...)`, batch variants) + manage documents for any contract. + - **Bound-token path** — `TOKEN_CONTRACT_ROLE` (the CMTA RuleEngine binding + pattern). The standard single-arg `IERC1643` functions (`setDocument(name,uri,hash)`, + `removeDocument(name)`) let a bound token manage its **own** namespace + (`_msgSender()`). Bind a token with `grantRole(TOKEN_CONTRACT_ROLE, token)`. +- **Events:** every write emits the standard `IERC1643` events **and** the optional + `DocumentUpdatedForContract` / `DocumentRemovedForContract` events (which add the + `smartContract` address). See `ERC-1643-proposition.md`. +- **ERC-2771:** meta-transaction (gasless) support; `_msgSender()` is used everywhere. +- **Access control:** `DEFAULT_ADMIN_ROLE` implicitly has every role (see the + `hasRole` override). +- **CMTAT integration:** since CMTAT v3, a token uses the engine via CMTAT's + `DocumentEngineModule` and `setDocumentEngine(engine)` (reads/writes are forwarded + keyed by the token address). Standard CMTAT standalone tokens store documents + on-chain instead and do **not** use this engine. + +## File tree + +``` +src/ +├── DocumentEngine.sol # Main contract: ERC-1643 impl, both management +│ # paths, batch functions, ERC-2771, access control +└── DocumentEngineInvariant.sol # Errors, roles (DOCUMENT_MANAGER_ROLE, + # TOKEN_CONTRACT_ROLE) and the optional multi-token events + +test/ +└── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin path, + # bound-token path, batch ops, CMTAT integration + # (CMTATDocumentEngineMock built on DocumentEngineModule) +``` + +Other important files: + +- `foundry.toml` — solc `0.8.34`, `evm_version = prague` (required by CMTAT v3). +- `remappings.txt` — `CMTAT/`, `RuleEngine/`, `OZ/`, `@openzeppelin/contracts-upgradeable/`. +- `CHANGELOG.md` — semver history; update on every release (current: `v0.4.0`). +- `ERC-1643-proposition.md` — proposed optional multi-token events / extension. +- `README.md` — full documentation and Surya schema. +- `doc/` — Surya diagrams/reports (`doc/script/`), Slither report, coverage. +- `lib/` — submodules: `CMTAT`, `RuleEngine`, `openzeppelin-contracts(-upgradeable)`, `forge-std`. + +## Dependencies (tested versions) + +- CMTAT `v3.3.0-rc1`, RuleEngine `v2.1.0` +- OpenZeppelin Contracts / Contracts Upgradeable `v5.6.1` +- Solidity `0.8.34`, Foundry + +## Common commands + +```bash +forge build # compile +forge test # run the test suite +forge fmt # format +forge test --gas-report +``` + +## Conventions + +- The contract `VERSION` constant (in `src/DocumentEngine.sol`) must match the + latest `CHANGELOG.md` entry on release. +- Bump `MAJOR` on incompatible proxy-storage / external-library or API changes, + `MINOR` for backward-compatible features, `PATCH` for backward-compatible fixes. +- A bound token can only ever affect its **own** document namespace — never break + that isolation. diff --git a/README.md b/README.md index 380053e..e6e8d21 100644 --- a/README.md +++ b/README.md @@ -13,36 +13,77 @@ The ERC-1643 defines a document with three attributes: - A generic URI (represented as a `string`) that could point to a website or other document portal. - The hash of the document contents associated with it on-chain. -A smart contract needs only to implement two functions from this standard, available in the interface [IERC1643](./contracts/interfaces/engined/draft-IERC1643.sol) to get the documents from the documentEngine. +A smart contract needs only to read documents from this standard through the interface [IERC1643](./lib/CMTAT/contracts/interfaces/tokenization/draft-IERC1643.sol) to get the documents from the documentEngine. Since CMTAT v3, `getDocument` returns a `Document` struct: ```solidity interface IERC1643 { -function getDocument(bytes32 _name) external view returns (string memory , bytes32, uint256); -function getAllDocuments() external view returns (bytes32[] memory); + struct Document { + string uri; + bytes32 documentHash; + uint256 lastModified; + } + + function getDocument(bytes32 name) external view returns (Document memory document); + function getAllDocuments() external view returns (bytes32[] memory documentNames_); + function setDocument(bytes32 name, string calldata uri, bytes32 documentHash) external; + function removeDocument(bytes32 name) external; } ``` -Use an external contract for your smart contract provides two advantages: +Using an external contract for your smart contract provides two advantages: - Reduce code size of your smart contract - Allow to manage documents for several different smart contracts -Warning: +### Two ways to manage documents -Since this engine allows to set documents for several different smart contracts, the functions to set documents take one supplementary arguments than defined in the ERC-1643. +The engine supports **two management paths** at the same time: -IERC1643 +**1. Admin path (`DOCUMENT_MANAGER_ROLE`).** Since the engine manages documents +for several different smart contracts, the admin functions take one supplementary +`address smartContract` argument compared to the ERC-1643: ```solidity -function setDocument(bytes32 _name, string _uri, bytes32 _documentHash) external; +// DocumentEngine (admin overloads) +function setDocument(address smartContract, bytes32 name_, string memory uri_, bytes32 documentHash_) external; +function removeDocument(address smartContract, bytes32 name_) external; ``` -DocumentEngine +**2. Bound-token path (`TOKEN_CONTRACT_ROLE`).** This implements the standard, +single-argument ERC-1643 functions. A token is *bound* to the engine by being +granted `TOKEN_CONTRACT_ROLE` (the same binding pattern as the CMTA +[RuleEngine](https://github.com/CMTA/RuleEngine)): ```solidity -function setDocument(address smartContract,bytes32 name_,string memory uri_, bytes32 documentHash_) +documentEngine.grantRole(TOKEN_CONTRACT_ROLE, address(token)); ``` +Once bound, the token manages its **own** documents (`msg.sender` is the token); +it can never affect another contract's documents: + +```solidity +// DocumentEngine (standard ERC-1643, scoped to msg.sender) +function setDocument(bytes32 name_, string calldata uri_, bytes32 documentHash_) external; +function removeDocument(bytes32 name_) external; +``` + +### Events + +On every write, the engine emits the standard `IERC1643` events **and** the +optional `DocumentUpdatedForContract` / `DocumentRemovedForContract` events, +which additionally carry the `smartContract` (token) address so off-chain +indexers can tell which contract a document belongs to during multi-contract +operations. See [ERC-1643-proposition.md](./ERC-1643-proposition.md) for the +proposed optional standard extension. + +### Integration with CMTAT + +Since CMTAT v3, the shipped standalone tokens store documents on-chain +(`DocumentERC1643Module`) and do not consume an external engine through their +constructor. To use this engine, a CMTAT token relies on the +`DocumentEngineModule` and is wired at runtime with `setDocumentEngine(engine)`; +reads/writes are then forwarded to the engine keyed by the token address. + ## Schema @@ -69,10 +110,12 @@ function setDocument(address smartContract,bytes32 name_,string memory uri_, byt | :----------------: | :------------------: | :----------------------------------------------: | :------------: | :-----------: | | └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | | | | | | | -| **DocumentEngine** | Implementation | IERC1643, DocumentEngineInvariant, AccessControl | | | +| **DocumentEngine** | Implementation | IERC1643, DocumentEngineInvariant, AccessControl, ERC2771Context | | | | └ | | Public ❗️ | 🛑 | NO❗️ | -| └ | setDocument | Public ❗️ | 🛑 | onlyRole | -| └ | removeDocument | External ❗️ | 🛑 | onlyRole | +| └ | setDocument | Public ❗️ | 🛑 | onlyRole (DOCUMENT_MANAGER_ROLE) | +| └ | removeDocument | External ❗️ | 🛑 | onlyRole (DOCUMENT_MANAGER_ROLE) | +| └ | setDocument | External ❗️ | 🛑 | onlyRole (TOKEN_CONTRACT_ROLE) | +| └ | removeDocument | External ❗️ | 🛑 | onlyRole (TOKEN_CONTRACT_ROLE) | | └ | batchSetDocuments | External ❗️ | 🛑 | onlyRole | | └ | batchSetDocuments | External ❗️ | 🛑 | onlyRole | | └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyRole | @@ -112,11 +155,12 @@ Please see the OpenGSN [documentation](https://docs.opengsn.org/contracts/#recei The toolchain includes the following components, where the versions are the latest ones that we tested: - Foundry -- Solidity 0.8.26 (via solc-js) -- OpenZeppelin Contracts (submodule) [v5.0.2](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.0.2) +- Solidity 0.8.34 (via solc-js), `evm_version = prague` +- OpenZeppelin Contracts (submodule) [v5.6.1](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.6.1) - Tests - - [CMTAT v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) - - OpenZeppelin Contracts Upgradeable(submodule) [v5.0.2](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v5.0.2) + - [CMTAT v3.3.0-rc1](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc1) + - [RuleEngine v2.1.0](https://github.com/CMTA/RuleEngine/releases/tag/v2.1.0) (binding-role reference) + - OpenZeppelin Contracts Upgradeable (submodule) [v5.6.1](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v5.6.1) ## Tools diff --git a/doc/ERCSpecification/ERC-1643-proposition.md b/doc/ERCSpecification/ERC-1643-proposition.md new file mode 100644 index 0000000..e6b57df --- /dev/null +++ b/doc/ERCSpecification/ERC-1643-proposition.md @@ -0,0 +1,129 @@ +# ERC-1643 — Proposition: optional multi-token document management + +> Status: **draft / proposition**. This document proposes optional additions to +> [ERC-1643](https://github.com/ethereum/EIPs/issues/1643) motivated by the +> `DocumentEngine` implementation in this repository. It is not part of the +> standard and is provided for discussion. + +## Context + +ERC-1643 describes document management **for a single contract**: the token that +implements the interface manages its own documents. In CMTAT v3, the standard +interface is: + +```solidity +interface IERC1643 { + struct Document { + string uri; + bytes32 documentHash; + uint256 lastModified; + } + + function getDocument(bytes32 name) external view returns (Document memory document); + function getAllDocuments() external view returns (bytes32[] memory documentNames_); + function setDocument(bytes32 name, string calldata uri, bytes32 documentHash) external; + function removeDocument(bytes32 name) external; + + event DocumentUpdated(bytes32 indexed name, string uri, bytes32 documentHash); + event DocumentRemoved(bytes32 indexed name, string uri, bytes32 documentHash); +} +``` + +A **document engine** is a different use case: a single external contract manages +documents **on behalf of several tokens**, keyed by the token address. This +reduces the code size of each token and lets one operator manage documents for a +whole fleet of tokens. + +For that multi-token use case, the standard events are **insufficient**: they only +carry the document `name`. When a single transaction sets documents for several +different tokens (batch operations), an off-chain indexer cannot tell from the +event alone which token a document belongs to — the emitting contract is always +the engine, not the token. + +## Proposition 1 — Optional multi-token events + +We propose two **optional** events that mirror the standard ones but add the +`smartContract` (token) address: + +```solidity +event DocumentUpdatedForContract( + address indexed smartContract, + bytes32 indexed name, + string uri, + bytes32 documentHash +); + +event DocumentRemovedForContract( + address indexed smartContract, + bytes32 indexed name, + string uri, + bytes32 documentHash +); +``` + +An engine that manages documents for several tokens SHOULD emit these events in +addition to the standard `DocumentUpdated` / `DocumentRemoved` events. The +standard events remain emitted for full backward compatibility with consumers +that only understand ERC-1643. + +### Important limitation: no ERC-165 discoverability + +Events are **not** part of a contract's ERC-165 interface id — `supportsInterface` +only covers function selectors. Consequently **there is no on-chain way for a +consumer to know whether a given implementation emits these optional events.** +A consumer that relies on them must obtain that information out of band (e.g. +documentation, a known implementation, or the optional extension interface below). + +## Proposition 2 — Optional multi-token management extension + +For implementations that want the multi-token capability to be +programmatically discoverable (via ERC-165) and callable, we propose an optional +extension interface. Unlike the events above, function selectors **are** part of +the ERC-165 interface id, so support can be detected on-chain. + +```solidity +interface IERC1643MultiDocument is IERC1643 { + /// @notice Get a document registered for `smartContract`. + function getDocument(address smartContract, bytes32 name) + external view returns (Document memory document); + + /// @notice Get all document names registered for `smartContract`. + function getAllDocuments(address smartContract) + external view returns (bytes32[] memory documentNames_); + + /// @notice Set or update a document for `smartContract`. + function setDocument(address smartContract, bytes32 name, string calldata uri, bytes32 documentHash) + external; + + /// @notice Remove a document for `smartContract`. + function removeDocument(address smartContract, bytes32 name) + external; + + event DocumentUpdatedForContract( + address indexed smartContract, + bytes32 indexed name, + string uri, + bytes32 documentHash + ); + event DocumentRemovedForContract( + address indexed smartContract, + bytes32 indexed name, + string uri, + bytes32 documentHash + ); +} +``` + +The single-argument functions inherited from `IERC1643` keep their standard +meaning: they operate on the caller (`msg.sender`) namespace, i.e. a token +managing its own documents. The address-scoped functions add the operator / +multi-token capability. + +## How this repository implements the proposition + +The `DocumentEngine` in this repository already follows Proposition 1: every +write emits both the standard event and the `...ForContract` variant. It also +provides the address-scoped functions of Proposition 2 (under +`DOCUMENT_MANAGER_ROLE`) and the single-argument, `msg.sender`-scoped functions +(under `TOKEN_CONTRACT_ROLE`, the RuleEngine binding pattern), though it does not +yet formally declare/expose an `IERC1643MultiDocument` interface id. diff --git a/doc/ERCSpecification/erc-1643.md b/doc/ERCSpecification/erc-1643.md new file mode 100644 index 0000000..20cc073 --- /dev/null +++ b/doc/ERCSpecification/erc-1643.md @@ -0,0 +1,158 @@ +--- +eip: 1643 +title: Document Management for Security Tokens +description: Interface to attach, update, remove, and enumerate legal or operational documents for token contracts. +author: Adam Dossa (@adamdossa), Pablo Ruiz (@pabloruiz55), Fabian Vogelsteller (@frozeman), Stephane Gosselin (@thegostep), Ryan Sauge (@rya-sge) +discussions-to: https://ethereum-magicians.org/t/erc-1643-document-management-standard-erc-1400/27437 +status: Draft +type: Standards Track +category: ERC +created: 2018-09-09 +--- + +## Abstract + +This ERC defines a standard interface for associating documents with a token contract and for notifying off-chain systems when those documents change. Documents can represent legal agreements, offering materials, disclosures, or other issuer-provided references needed for security token operations. + +## Motivation + +Security tokens commonly represent assets with legal rights and obligations that depend on external documents. Wallets, exchanges, custodians, and compliance tools need a predictable way to discover those documents and track updates. + +Without a standard, each implementation exposes different storage and retrieval methods, increasing integration cost and operational risk. A common interface allows ecosystem participants to read and monitor document metadata consistently. + +Within security token frameworks, the document management component highlights that security tokens usually have associated documentation such as offering documents and legend details. The ability to set, remove, and retrieve these documents, with events emitted on those actions, allows investors and integrators to remain up to date. + +This ERC intentionally does not define an on-chain mechanism for investors to attest they have read or agreed to any document. + +Although originally designed for security tokens built on [ERC-20](./eip-20.md) as part of a broader real-world asset token suite, this interface is not restricted to that context. It can be adopted by any token standard, including [ERC-721](./eip-721.md) non-fungible tokens and [ERC-1155](./eip-1155.md) multi-tokens, as well as by decentralized applications, vaults, and any other on-chain product that requires structured document management. + +Historically, this proposal was authored as part of a broader security token standards suite that had not yet been merged in this repository when this proposal was added. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", and "MAY" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +Implementations MUST support querying and subscribing to updates on any relevant documentation for the security. + +A document entry is identified by a name (`bytes32`) and stores: + +- A URI (`string`) pointing to the document location. +- A content hash (`bytes32`) for integrity checks. +- A last-modified timestamp (`uint256`) set when the entry is written. + +### Interface + +```solidity +/// @title IERC1643 Document Management +interface IERC1643 { + /// @notice Reverts when `setDocument` is called with `name == bytes32(0)`. + error ERC1643InvalidName(); + + /// @notice Reverts when `removeDocument` is called for a missing document. + error ERC1643MissingDocument(); + + /// @notice Returns metadata for a document identified by `name`. + /// @return uri Document location. + /// @return documentHash Hash of the document contents. + /// @return lastModified Last update timestamp. + function getDocument(bytes32 name) external view returns (string memory uri, bytes32 documentHash, uint256 lastModified); + + /// @notice Creates or updates a document entry. + /// @dev MUST emit `DocumentUpdated` on success. + function setDocument(bytes32 name, string calldata uri, bytes32 documentHash) external; + + /// @notice Removes an existing document entry. + /// @dev MUST emit `DocumentRemoved` on success. + function removeDocument(bytes32 name) external; + + /// @notice Returns all document names currently tracked by the contract. + function getAllDocuments() external view returns (bytes32[] memory documentNames); + + /// @notice Emitted when a document is created or updated. + event DocumentUpdated(bytes32 indexed name, string uri, bytes32 documentHash); + + /// @notice Emitted when a document is removed. + event DocumentRemoved(bytes32 indexed name, string uri, bytes32 documentHash); +} +``` + +### Interface Detection ([ERC-165](./eip-165.md)) + +Implementations SHOULD support ERC-165 interface detection. + +When ERC-165 is implemented, `supportsInterface` SHOULD return `true` for `type(IERC1643).interfaceId` and for the ERC-165 interface id. + +### Function Requirements + +- `getDocument`: + - MUST return the latest values for the provided document name. + - MUST return empty values when the entry does not exist (`""`, `bytes32(0)`, `0`). + - MUST NOT revert solely because the entry does not exist. + +- `setDocument`: + - MUST create a new entry when `name` is not present. + - MUST overwrite the existing entry when `name` already exists. + - MUST update the stored last-modified timestamp. + - MUST emit `DocumentUpdated` after state changes. + - MUST revert if the update cannot be persisted. + - SHOULD revert when `name == bytes32(0)` to avoid ambiguous/default-key usage. + - `uri` and `documentHash` MAY be empty (`""` and `bytes32(0)`), depending on issuer workflow and document lifecycle stage. + - Implementations MAY decide to reject empty `uri` and/or empty `documentHash` based on policy requirements. + - Implementations SHOULD use the custom error defined in the interface (`ERC1643InvalidName()`) when rejecting `name == bytes32(0)`. + - Implementations MAY use different error names/signatures than those shown in this specification. + +- `removeDocument`: + - MUST remove the entry identified by `name`. + - MUST emit `DocumentRemoved` with the removed metadata. + - MUST revert if removal cannot be completed. + - Implementations SHOULD use the custom error defined in the interface (`ERC1643MissingDocument()`) when the named document does not exist. + - Implementations MAY use different error names/signatures than those shown in this specification. + +- `getAllDocuments`: + - MUST include every document name added by `setDocument` and not removed by `removeDocument`. + - MUST NOT include removed document names. + +## Rationale + +The standard uses `bytes32` names to keep keys compact and deterministic, while leaving naming conventions to implementations. A URI-based pointer is used instead of on-chain document storage to avoid high gas costs and to support existing off-chain document systems. + +Including a document hash enables clients to verify that fetched off-chain content matches issuer-published metadata. Emitting update and removal events supports indexing and near-real-time monitoring without repeated full-state polling. + +While a human-readable document title cannot always be represented directly in `bytes32` without hashing or canonicalization, `bytes32` remains practical for on-chain identifiers because fixed-size values can be compared directly (`a == b`). By contrast, `string` comparisons generally require hashing (for example, `keccak256(bytes(s))`), which increases contract code size and gas usage when repeated comparisons are needed on-chain, such as locating and removing a document name from an array. + +## Backwards Compatibility + +This ERC is additive and does not alter base token transfer semantics. It can be implemented alongside existing token standards and permissioning systems without changing their core behavior. + +## Test Cases + +Implementations should verify at least the following: + +- Adding a new document and reading it through `getDocument`. +- Updating an existing document and validating changed URI/hash/timestamp. +- Removing a document and ensuring it is no longer returned by `getAllDocuments`. +- Emission of `DocumentUpdated` on create/update and `DocumentRemoved` on delete. +- Enumeration consistency after multiple add/update/remove operations. + +## Reference Implementation + +The interface is provided in [the reference interface](../assets/eip-1643/src/erc-1643/IERC1643.sol). A reusable abstract module implementing the full interface is provided in [the reference module](../assets/eip-1643/src/erc-1643/ERC1643.sol). Example integrations attaching the module to [ERC-20](./eip-20.md) and [ERC-721](./eip-721.md) tokens are provided in [the ERC-20 example](../assets/eip-1643/src/ERC20DocumentToken.sol) and [the ERC-721 example](../assets/eip-1643/src/ERC721DocumentToken.sol). These examples use the OpenZeppelin library and restrict document mutation to the contract owner. They are provided for educational purposes only and have not been audited. + +The module maintains: + +- Mapping from `bytes32` name to document metadata. +- Array/set for enumeration of active names. +- Index tracking to support O(1) removals from the enumeration set. + +## Security Considerations + +- Document URIs may reference mutable off-chain content. Consumers are strongly encouraged to verify content using the published `documentHash` and trusted retrieval channels. +- Implementations should protect `setDocument` and `removeDocument` with appropriate authorization, otherwise unauthorized actors can modify legal or operational references. +- Applications should treat event streams as advisory and reconcile against on-chain state when correctness is critical. +- Document names may not always fit cleanly into `bytes32`, especially for long legal titles. Implementations should avoid lossy truncation of human-readable names; using a deterministic hash-based identifier (for example, the document content hash or a hash of a canonical full title) as the `bytes32` name is a safer alternative. +- The custom errors `ERC1643InvalidName()` and `ERC1643MissingDocument()` are defined in this interface but were absent from the original [ERC-1643](./eip-1643.md) proposal text. Older implementations may not define these errors and may instead revert with strings or implementation-specific error patterns. Integrators should not assume all ERC-1643 contracts expose identical revert data. +- ERC-165 interface detection was also not part of the earlier ERC-1643 draft text. Older implementations may not expose `supportsInterface` for ERC-165 or `IERC1643`, so integrators should treat ERC-165 support as optional when interacting with legacy deployments. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/doc/slither-report.md b/doc/slither-report.md deleted file mode 100644 index cc8c1dd..0000000 --- a/doc/slither-report.md +++ /dev/null @@ -1,38 +0,0 @@ -**THIS CHECKLIST IS NOT COMPLETE**. Use `--show-ignored-findings` to show all the results. -Summary - - [dead-code](#dead-code) (1 results) (Informational) - - [solc-version](#solc-version) (1 results) (Informational) -## dead-code - -> Acknowledge - -Impact: Informational -Confidence: Medium - - - [ ] ID-0 -[DocumentEngine._msgData()](src/DocumentEngine.sol#L265-L272) is never used and should be removed - -src/DocumentEngine.sol#L265-L272 - -## solc-version - -> Acknowledge - -Impact: Informational -Confidence: High - - [ ] ID-1 - Version constraint ^0.8.20 contains known severe issues (https://solidity.readthedocs.io/en/latest/bugs.html) - - VerbatimInvalidDeduplication - - FullInlinerNonExpressionSplitArgumentEvaluationOrder - - MissingSideEffectsOnSelectorAccess. - It is used by: - - lib/CMTAT/contracts/interfaces/engine/draft-IERC1643.sol#3 - - lib/openzeppelin-contracts/contracts/access/AccessControl.sol#4 - - lib/openzeppelin-contracts/contracts/access/IAccessControl.sol#4 - - lib/openzeppelin-contracts/contracts/metatx/ERC2771Context.sol#4 - - lib/openzeppelin-contracts/contracts/utils/Context.sol#4 - - lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol#4 - - lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol#4 - - src/DocumentEngine.sol#2 - - src/DocumentEngineInvariant.sol#2 - diff --git a/foundry.lock b/foundry.lock new file mode 100644 index 0000000..be96ac4 --- /dev/null +++ b/foundry.lock @@ -0,0 +1,20 @@ +{ + "lib/CMTAT": { + "rev": "e8048d43b0299afd83f150d3725ab299994b4271" + }, + "lib/RuleEngine": { + "tag": { + "name": "v2.1.0", + "rev": "461d32fca6cf501d6c15f3aed5f18855b2a6581c" + } + }, + "lib/forge-std": { + "rev": "1714bee72e286e73f76e320d110e0eaf5c4e649d" + }, + "lib/openzeppelin-contracts": { + "rev": "dbb6104ce834628e473d2173bbc9d47f81a9eec3" + }, + "lib/openzeppelin-contracts-upgradeable": { + "rev": "723f8cab09cdae1aca9ec9cc1cfa040c2d4b06c1" + } +} \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index acc6e5a..8945d0e 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,10 +1,10 @@ [profile.default] -solc = "0.8.26" +solc = "0.8.34" src = "src" out = "out" libs = ["lib"] optimizer = true optimizer_runs = 200 -evm_version = 'cancun' +evm_version = 'prague' # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/lib/CMTAT b/lib/CMTAT index e8048d4..580d477 160000 --- a/lib/CMTAT +++ b/lib/CMTAT @@ -1 +1 @@ -Subproject commit e8048d43b0299afd83f150d3725ab299994b4271 +Subproject commit 580d4776e4cbb857b2da7d83fd79144ae7e47557 diff --git a/lib/RuleEngine b/lib/RuleEngine new file mode 160000 index 0000000..461d32f --- /dev/null +++ b/lib/RuleEngine @@ -0,0 +1 @@ +Subproject commit 461d32fca6cf501d6c15f3aed5f18855b2a6581c diff --git a/lib/forge-std b/lib/forge-std index 1714bee..f73c73d 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 1714bee72e286e73f76e320d110e0eaf5c4e649d +Subproject commit f73c73d2018eb6a111f35e4dae7b4f27401e9421 diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index dbb6104..5fd1781 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit dbb6104ce834628e473d2173bbc9d47f81a9eec3 +Subproject commit 5fd1781b1454fd1ef8e722282f86f9293cacf256 diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable index 723f8ca..7bf4727 160000 --- a/lib/openzeppelin-contracts-upgradeable +++ b/lib/openzeppelin-contracts-upgradeable @@ -1 +1 @@ -Subproject commit 723f8cab09cdae1aca9ec9cc1cfa040c2d4b06c1 +Subproject commit 7bf4727aacdbfaa0f36cbd664654d0c9e1dc52bf diff --git a/remappings.txt b/remappings.txt index b0803f5..31bf2f2 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,3 +1,4 @@ CMTAT/=lib/CMTAT/contracts/ +RuleEngine/=lib/RuleEngine/src/ OZ/=lib/openzeppelin-contracts/contracts/ @openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ \ No newline at end of file diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index 3f4fff9..2d18acd 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.20; import "OZ/access/AccessControl.sol"; import "OZ/metatx/ERC2771Context.sol"; -import "CMTAT/interfaces/engine/draft-IERC1643.sol"; +import "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; import "./DocumentEngineInvariant.sol"; /** @@ -20,7 +20,7 @@ contract DocumentEngine is * @notice * Get the current version of the smart contract */ - string public constant VERSION = "0.3.0"; + string public constant VERSION = "0.4.0"; // Mapping from contract addresses to document names to their corresponding Document structs mapping(address => mapping(bytes32 => Document)) private _documents; mapping(address => bytes32[]) private _documentNames; @@ -62,6 +62,33 @@ contract DocumentEngine is _removeDocument(smartContract, name_); } + /* ============ ERC-1643 (bound token) ============ */ + + /** + * @notice ERC-1643 function to set or update a document for the caller. + * @dev The document is stored under the caller (`_msgSender()`) namespace. + * The caller must be a token bound to this engine (`TOKEN_CONTRACT_ROLE`), + * following the RuleEngine binding pattern. A bound token can only manage + * its own documents; it can never affect another contract's documents. + */ + function setDocument( + bytes32 name_, + string calldata uri_, + bytes32 documentHash_ + ) external override onlyRole(TOKEN_CONTRACT_ROLE) { + _setDocument(_msgSender(), name_, uri_, documentHash_); + } + + /** + * @notice ERC-1643 function to remove a document for the caller. + * @dev See {setDocument}. Scoped to the caller (`_msgSender()`) namespace. + */ + function removeDocument( + bytes32 name_ + ) external override onlyRole(TOKEN_CONTRACT_ROLE) { + _removeDocument(_msgSender(), name_); + } + /** * @notice Batch version of setDocument to handle multiple documents at once */ @@ -141,12 +168,12 @@ contract DocumentEngine is } /** - * @notice Public function to get a document from msg.sender + * @notice ERC-1643 function to get a document for the caller (`_msgSender()`) */ function getDocument( bytes32 name_ - ) external view override returns (string memory, bytes32, uint256) { - return _getDocument(msg.sender, name_); + ) external view override returns (Document memory) { + return _getDocument(_msgSender(), name_); } /** @@ -155,7 +182,7 @@ contract DocumentEngine is function getDocument( address smartContract, bytes32 name_ - ) external view returns (string memory, bytes32, uint256) { + ) external view returns (Document memory) { return _getDocument(smartContract, name_); } @@ -168,7 +195,7 @@ contract DocumentEngine is override returns (bytes32[] memory) { - return _documentNames[msg.sender]; + return _documentNames[_msgSender()]; } /** @@ -205,9 +232,8 @@ contract DocumentEngine is function _getDocument( address smartContract, bytes32 name_ - ) internal view returns (string memory, bytes32, uint256) { - Document memory doc = _documents[smartContract][name_]; - return (doc.uri, doc.documentHash, doc.lastModified); + ) internal view returns (Document memory) { + return _documents[smartContract][name_]; } /** @@ -231,7 +257,15 @@ contract DocumentEngine is function _removeDocument(address smartContract, bytes32 name_) internal { Document memory doc = _documents[smartContract][name_]; - emit DocumentRemoved(smartContract, name_, doc.uri, doc.documentHash); + // Standard ERC-1643 event + emit DocumentRemoved(name_, doc.uri, doc.documentHash); + // Optional multi-token event (see ERC-1643-proposition.md) + emit DocumentRemovedForContract( + smartContract, + name_, + doc.uri, + doc.documentHash + ); delete _documents[smartContract][name_]; _removeDocumentName(smartContract, name_); @@ -251,7 +285,15 @@ contract DocumentEngine is doc.uri = uri_; doc.documentHash = documentHash_; doc.lastModified = block.timestamp; - emit DocumentUpdated(smartContract, name_, uri_, documentHash_); + // Standard ERC-1643 event + emit DocumentUpdated(name_, uri_, documentHash_); + // Optional multi-token event (see ERC-1643-proposition.md) + emit DocumentUpdatedForContract( + smartContract, + name_, + uri_, + documentHash_ + ); } /*////////////////////////////////////////////////////////////// diff --git a/src/DocumentEngineInvariant.sol b/src/DocumentEngineInvariant.sol index b3836d8..2d229fd 100644 --- a/src/DocumentEngineInvariant.sol +++ b/src/DocumentEngineInvariant.sol @@ -6,26 +6,35 @@ contract DocumentEngineInvariant { error InvalidInputLength(); error AdminWithAddressZeroNotAllowed(); - event DocumentUpdated( - address smartContract, - bytes32 name, + /** + * @notice Optional multi-token events emitted in addition to the standard + * `IERC1643.DocumentUpdated` / `IERC1643.DocumentRemoved` events. + * @dev Because this engine manages documents on behalf of several smart + * contracts (tokens), the standard events - which only carry the document + * `name` - are not sufficient to identify which contract a document belongs + * to. These events add the `smartContract` address for off-chain indexers. + * See `ERC-1643-proposition.md` for the proposed optional standard extension. + */ + event DocumentUpdatedForContract( + address indexed smartContract, + bytes32 indexed name, string uri, bytes32 documentHash ); - event DocumentRemoved( - address smartContract, - bytes32 name, + event DocumentRemovedForContract( + address indexed smartContract, + bytes32 indexed name, string uri, bytes32 documentHash ); - // Document structure - struct Document { - string uri; - bytes32 documentHash; - uint256 lastModified; - } - + // Role allowed to manage documents on behalf of any smart contract (admin path) bytes32 public constant DOCUMENT_MANAGER_ROLE = keccak256("DOCUMENT_MANAGER_ROLE"); + + // Role granted to a token bound to the engine, allowing it to manage its own + // documents through the standard ERC-1643 functions (msg.sender is the token). + // Mirrors the RuleEngine binding pattern (CMTA/RuleEngine `TOKEN_CONTRACT_ROLE`). + bytes32 public constant TOKEN_CONTRACT_ROLE = + keccak256("TOKEN_CONTRACT_ROLE"); } diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index fda9890..abed6b5 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -5,7 +5,22 @@ import "forge-std/Test.sol"; import "../src/DocumentEngine.sol"; import "../src/DocumentEngineInvariant.sol"; import "OZ/access/AccessControl.sol"; -import "CMTAT/CMTAT_STANDALONE.sol"; +import {DocumentEngineModule} from "CMTAT/modules/wrapper/options/DocumentEngineModule.sol"; + +/** + * @dev Minimal token wired to CMTAT's official `DocumentEngineModule`. + * + * Since CMTAT v3, the shipped standalone tokens store documents on-chain + * (`DocumentERC1643Module`) and no longer consume an external document engine + * through their constructor. Integration with an external `DocumentEngine` now + * goes through `DocumentEngineModule`, which this mock exercises with real + * CMTAT code: reads/writes are forwarded to the engine keyed by `msg.sender`. + */ +contract CMTATDocumentEngineMock is DocumentEngineModule { + // No access restriction for the mock: document management is authorized for anyone. + function _authorizeDocumentManagement() internal override {} +} + contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { DocumentEngine public documentEngine; address public admin = address(0x1); @@ -18,7 +33,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { bytes32 public documentHash = keccak256("doc1Hash"); bytes32 public constant DOCUMENT_ROLE = keccak256("DOCUMENT_ROLE"); address AddressZero = address(0); - CMTAT_STANDALONE cmtat; + function setUp() public { documentEngine = new DocumentEngine(admin, AddressZero); vm.prank(admin); @@ -28,34 +43,6 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentURI, documentHash ); - - // CMTAT - ICMTATConstructor.ERC20Attributes - memory erc20Attributes = ICMTATConstructor.ERC20Attributes( - "CMTA Token", - "CMTAT", - 0 - ); - ICMTATConstructor.BaseModuleAttributes - memory baseModuleAttributes = ICMTATConstructor - .BaseModuleAttributes( - "CMTAT_ISIN", - "https://cmta.ch", - "CMTAT_info" - ); - ICMTATConstructor.Engine memory engines = ICMTATConstructor.Engine( - IRuleEngine(AddressZero), - IDebtEngine(AddressZero), - IAuthorizationEngine(AddressZero), - IERC1643(AddressZero) - ); - cmtat = new CMTAT_STANDALONE( - AddressZero, - admin, - erc20Attributes, - baseModuleAttributes, - engines - ); } /*////////////////////////////////////////////////////////////// @@ -177,17 +164,24 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { } /*////////////////////////////////////////////////////////////// - Get + Get //////////////////////////////////////////////////////////////*/ - function testGetAllDocuments() public view { + function testGetAllDocuments() public { bytes32[] memory docs = documentEngine.getAllDocuments(testContract); assertEq(docs.length, 1); assertEq(docs[0], documentName); } + /*////////////////////////////////////////////////////////////// + CMTAT integration (external engine via DocumentEngineModule) + //////////////////////////////////////////////////////////////*/ + function testCanReturnCMTATDocument() public { - // Arrange + // Arrange: a CMTAT-style token bound to the engine + CMTATDocumentEngineMock cmtat = new CMTATDocumentEngineMock(); + cmtat.setDocumentEngine(documentEngine); + uint256 lastModif = block.timestamp; vm.prank(admin); documentEngine.setDocument( @@ -196,19 +190,76 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentURI, documentHash ); - vm.prank(admin); - cmtat.setDocumentEngine(documentEngine); - // Call from CMTAT, return document + // Call from CMTAT, forwarded to the engine bytes32[] memory docs = cmtat.getAllDocuments(); assertEq(docs.length, 1); assertEq(docs[0], documentName); - (string memory uri, bytes32 hash, uint256 lastModified) = cmtat - .getDocument(documentName); - assertEq(uri, documentURI); - assertEq(hash, documentHash); - assertEq(lastModif, lastModified); + IERC1643.Document memory doc = cmtat.getDocument(documentName); + assertEq(doc.uri, documentURI); + assertEq(doc.documentHash, documentHash); + assertEq(doc.lastModified, lastModif); + } + + /*////////////////////////////////////////////////////////////// + Bound token (RuleEngine binding pattern, TOKEN_CONTRACT_ROLE) + //////////////////////////////////////////////////////////////*/ + + function testBoundTokenCanManageOwnDocument() public { + // Bind the token to the engine + vm.prank(admin); + documentEngine.grantRole(TOKEN_CONTRACT_ROLE, testContract); + + // The bound token manages its own document namespace (msg.sender) + bytes32 selfName = keccak256("self-doc"); + string memory selfURI = "https://example.com/self"; + bytes32 selfHash = keccak256("selfHash"); + + vm.prank(testContract); + documentEngine.setDocument(selfName, selfURI, selfHash); + + IERC1643.Document memory doc = documentEngine.getDocument( + testContract, + selfName + ); + assertEq(doc.uri, selfURI); + assertEq(doc.documentHash, selfHash); + assertEq(doc.lastModified, block.timestamp); + + // and can remove it + vm.prank(testContract); + documentEngine.removeDocument(selfName); + doc = documentEngine.getDocument(testContract, selfName); + assertEq(doc.uri, ""); + assertEq(doc.documentHash, ""); + assertEq(doc.lastModified, 0); + } + + function testUnboundContractCannotSetOwnDocument() public { + bytes32 selfName = keccak256("self-doc"); + vm.prank(attacker); + vm.expectRevert( + abi.encodeWithSelector( + AccessControlUnauthorizedAccount.selector, + attacker, + TOKEN_CONTRACT_ROLE + ) + ); + documentEngine.setDocument(selfName, documentURI, documentHash); + } + + function testUnboundContractCannotRemoveOwnDocument() public { + bytes32 selfName = keccak256("self-doc"); + vm.prank(attacker); + vm.expectRevert( + abi.encodeWithSelector( + AccessControlUnauthorizedAccount.selector, + attacker, + TOKEN_CONTRACT_ROLE + ) + ); + documentEngine.removeDocument(selfName); } /*////////////////////////////////////////////////////////////// @@ -224,11 +275,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentHash ); - (string memory uri, bytes32 hash, uint256 lastModified) = documentEngine - .getDocument(testContract, documentName); - assertEq(uri, documentURI); - assertEq(hash, documentHash); - assertEq(lastModif, lastModified); + IERC1643.Document memory doc = documentEngine.getDocument( + testContract, + documentName + ); + assertEq(doc.uri, documentURI); + assertEq(doc.documentHash, documentHash); + assertEq(doc.lastModified, lastModif); } function testAdminCanSetDocumentAgain() public { @@ -256,11 +309,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { ); // Assert - (string memory uri, bytes32 hash, uint256 lastModified) = documentEngine - .getDocument(testContract, documentName); - assertEq(uri, documentURIV2); - assertEq(hash, documentHashV2); - assertEq(lastModif, lastModified); + IERC1643.Document memory doc = documentEngine.getDocument( + testContract, + documentName + ); + assertEq(doc.uri, documentURIV2); + assertEq(doc.documentHash, documentHashV2); + assertEq(doc.lastModified, lastModif); docs = documentEngine.getAllDocuments(testContract); assertEq(docs.length, 1); assertEq(docs[0], documentName); @@ -287,24 +342,22 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentEngine.batchSetDocuments(smartContracts, names, uris, hashes); // Check the first document - ( - string memory uri1, - bytes32 hash1, - uint256 lastModified1 - ) = documentEngine.getDocument(testContract, documentName); - assertEq(uri1, documentURI); - assertEq(hash1, documentHash); - assertEq(lastModified1, block.timestamp); + IERC1643.Document memory doc1 = documentEngine.getDocument( + testContract, + documentName + ); + assertEq(doc1.uri, documentURI); + assertEq(doc1.documentHash, documentHash); + assertEq(doc1.lastModified, block.timestamp); // Check the second document - ( - string memory uri2, - bytes32 hash2, - uint256 lastModified2 - ) = documentEngine.getDocument(anotherSmartContract, names[1]); - assertEq(uri2, uris[1]); - assertEq(hash2, hashes[1]); - assertEq(lastModified2, block.timestamp); + IERC1643.Document memory doc2 = documentEngine.getDocument( + anotherSmartContract, + names[1] + ); + assertEq(doc2.uri, uris[1]); + assertEq(doc2.documentHash, hashes[1]); + assertEq(doc2.lastModified, block.timestamp); } function testAdminCanBatchSetDocumentsForTheSameContract() public { @@ -328,24 +381,22 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentEngine.batchSetDocuments(smartContracts, names, uris, hashes); // Check the first document - ( - string memory uri1, - bytes32 hash1, - uint256 lastModified1 - ) = documentEngine.getDocument(testContract, documentName); - assertEq(uri1, documentURI); - assertEq(hash1, documentHash); - assertEq(lastModified1, block.timestamp); + IERC1643.Document memory doc1 = documentEngine.getDocument( + testContract, + documentName + ); + assertEq(doc1.uri, documentURI); + assertEq(doc1.documentHash, documentHash); + assertEq(doc1.lastModified, block.timestamp); // Check the second document - ( - string memory uri2, - bytes32 hash2, - uint256 lastModified2 - ) = documentEngine.getDocument(testContract, names[1]); - assertEq(uri2, uris[1]); - assertEq(hash2, hashes[1]); - assertEq(lastModified2, block.timestamp); + IERC1643.Document memory doc2 = documentEngine.getDocument( + testContract, + names[1] + ); + assertEq(doc2.uri, uris[1]); + assertEq(doc2.documentHash, hashes[1]); + assertEq(doc2.lastModified, block.timestamp); } function testCannotAddBatchDocumentIfLengthMismatch_A() public { @@ -412,11 +463,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { // Check that both documents are removed // Check the second document - (string memory uri, bytes32 hash, uint256 lastModified) = documentEngine - .getDocument(testContract, documentName); - assertEq(uri, ""); - assertEq(hash, ""); - assertEq(lastModified, 0); + IERC1643.Document memory doc = documentEngine.getDocument( + testContract, + documentName + ); + assertEq(doc.uri, ""); + assertEq(doc.documentHash, ""); + assertEq(doc.lastModified, 0); bytes32[] memory docs = documentEngine.getAllDocuments(testContract); assertEq(docs.length, 0); } @@ -439,22 +492,23 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { // Check that both documents are removed // Check the second document - (string memory uri, bytes32 hash, uint256 lastModified) = documentEngine - .getDocument(testContract, documentName); - assertEq(uri, ""); - assertEq(hash, ""); - assertEq(lastModified, 0); + IERC1643.Document memory doc = documentEngine.getDocument( + testContract, + documentName + ); + assertEq(doc.uri, ""); + assertEq(doc.documentHash, ""); + assertEq(doc.lastModified, 0); bytes32[] memory docs = documentEngine.getAllDocuments(testContract); assertEq(docs.length, 0); - ( - string memory uri2, - bytes32 hash2, - uint256 lastModified2 - ) = documentEngine.getDocument(anotherSmartContract, names[1]); - assertEq(uri2, ""); - assertEq(hash2, ""); - assertEq(lastModified2, 0); + IERC1643.Document memory doc2 = documentEngine.getDocument( + anotherSmartContract, + names[1] + ); + assertEq(doc2.uri, ""); + assertEq(doc2.documentHash, ""); + assertEq(doc2.lastModified, 0); docs = documentEngine.getAllDocuments(anotherSmartContract); assertEq(docs.length, 0); } @@ -500,24 +554,22 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentEngine.batchSetDocuments(testContract, names, uris, hashes); // Check the first document - ( - string memory uri1, - bytes32 hash1, - uint256 lastModified1 - ) = documentEngine.getDocument(testContract, documentName); - assertEq(uri1, documentURI); - assertEq(hash1, documentHash); - assertEq(lastModified1, block.timestamp); + IERC1643.Document memory doc1 = documentEngine.getDocument( + testContract, + documentName + ); + assertEq(doc1.uri, documentURI); + assertEq(doc1.documentHash, documentHash); + assertEq(doc1.lastModified, block.timestamp); // Check the second document - ( - string memory uri2, - bytes32 hash2, - uint256 lastModified2 - ) = documentEngine.getDocument(testContract, names[1]); - assertEq(uri2, uris[1]); - assertEq(hash2, hashes[1]); - assertEq(lastModified2, block.timestamp); + IERC1643.Document memory doc2 = documentEngine.getDocument( + testContract, + names[1] + ); + assertEq(doc2.uri, uris[1]); + assertEq(doc2.documentHash, hashes[1]); + assertEq(doc2.lastModified, block.timestamp); } function testAdminCanBatchRemoveDocumentsForOnlyOneContract() public { @@ -534,21 +586,22 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { // Check that both documents are removed // Check the second document - (string memory uri, bytes32 hash, uint256 lastModified) = documentEngine - .getDocument(testContract, documentName); - assertEq(uri, ""); - assertEq(hash, ""); - assertEq(lastModified, 0); + IERC1643.Document memory doc = documentEngine.getDocument( + testContract, + documentName + ); + assertEq(doc.uri, ""); + assertEq(doc.documentHash, ""); + assertEq(doc.lastModified, 0); bytes32[] memory docs = documentEngine.getAllDocuments(testContract); assertEq(docs.length, 0); - ( - string memory uri2, - bytes32 hash2, - uint256 lastModified2 - ) = documentEngine.getDocument(testContract, names[1]); - assertEq(uri2, ""); - assertEq(hash2, ""); - assertEq(lastModified2, 0); + IERC1643.Document memory doc2 = documentEngine.getDocument( + testContract, + names[1] + ); + assertEq(doc2.uri, ""); + assertEq(doc2.documentHash, ""); + assertEq(doc2.lastModified, 0); } function testCannotRemoveBatchDocumentIfEmptyLengthForOnlyOneContract() From afb03c2ad606fdaca4b91e1c8bc9fcde50b265fd Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 07:17:17 +0200 Subject: [PATCH 02/47] refactor: delegate access control to overridable _authorize hooks (CMTAT/RuleEngine pattern) --- AGENTS.md | 7 +++++ CHANGELOG.md | 1 + CLAUDE.md | 7 +++++ README.md | 38 ++++++++++++++++++----- src/DocumentEngine.sol | 64 +++++++++++++++++++++++++++++++++------ test/DocumentEngine.t.sol | 44 +++++++++++++++++++++++++++ 6 files changed, 144 insertions(+), 17 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ca68820..ca26a28 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,6 +33,13 @@ addressed by a `bytes32` name. - **ERC-2771:** meta-transaction (gasless) support; `_msgSender()` is used everywhere. - **Access control:** `DEFAULT_ADMIN_ROLE` implicitly has every role (see the `hasRole` override). +- **Flexible access control (CMTAT / RuleEngine pattern):** restricted functions + use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to + overridable `internal virtual` hooks `_authorizeDocumentManagement()` / + `_authorizeBoundTokenDocumentManagement()` (default `DOCUMENT_MANAGER_ROLE` / + `TOKEN_CONTRACT_ROLE`). Keep the management implementation separate from the + authorization logic — change *who* is authorized by overriding a hook, not by + editing the management functions. - **CMTAT integration:** since CMTAT v3, a token uses the engine via CMTAT's `DocumentEngineModule` and `setDocumentEngine(engine)` (reads/writes are forwarded keyed by the token address). Standard CMTAT standalone tokens store documents diff --git a/CHANGELOG.md b/CHANGELOG.md index 576d56c..0ee30c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) - **Bound-token document management (RuleEngine binding pattern)**: implement the now-mandatory `IERC1643.setDocument(name, uri, hash)` and `removeDocument(name)`. They are gated by a new `TOKEN_CONTRACT_ROLE` and scoped to the caller (`_msgSender()`) own namespace. A token bound with `grantRole(TOKEN_CONTRACT_ROLE, token)` manages its own documents and can never affect another contract's documents. The existing admin overloads (explicit `address`, `DOCUMENT_MANAGER_ROLE`) are unchanged, so both systems work side by side. - **Optional multi-token events**: alongside the standard `IERC1643` events, the engine now also emits `DocumentUpdatedForContract` / `DocumentRemovedForContract`, which carry the `smartContract` (token) address so off-chain indexers can tell which contract a document belongs to during multi-contract operations. See [`ERC-1643-proposition.md`](./ERC-1643-proposition.md) for the proposed optional standard extension. +- **Flexible access control (CMTAT / RuleEngine pattern)**: the restricted functions now use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to overridable `internal virtual` authorization hooks `_authorizeDocumentManagement()` / `_authorizeBoundTokenDocumentManagement()` (default: `DOCUMENT_MANAGER_ROLE` / `TOKEN_CONTRACT_ROLE`). This separates the document-management implementation from the authorization logic, so a subclass can change *who* is authorized without touching the management functions. Default behavior is unchanged. ### Notes / bottlenecks diff --git a/CLAUDE.md b/CLAUDE.md index ca68820..ca26a28 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,6 +33,13 @@ addressed by a `bytes32` name. - **ERC-2771:** meta-transaction (gasless) support; `_msgSender()` is used everywhere. - **Access control:** `DEFAULT_ADMIN_ROLE` implicitly has every role (see the `hasRole` override). +- **Flexible access control (CMTAT / RuleEngine pattern):** restricted functions + use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to + overridable `internal virtual` hooks `_authorizeDocumentManagement()` / + `_authorizeBoundTokenDocumentManagement()` (default `DOCUMENT_MANAGER_ROLE` / + `TOKEN_CONTRACT_ROLE`). Keep the management implementation separate from the + authorization logic — change *who* is authorized by overriding a hook, not by + editing the management functions. - **CMTAT integration:** since CMTAT v3, a token uses the engine via CMTAT's `DocumentEngineModule` and `setDocumentEngine(engine)` (reads/writes are forwarded keyed by the token address). Standard CMTAT standalone tokens store documents diff --git a/README.md b/README.md index e6e8d21..9b8e488 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,28 @@ function setDocument(bytes32 name_, string calldata uri_, bytes32 documentHash_) function removeDocument(bytes32 name_) external; ``` +### Flexible access control + +Following the CMTAT / [RuleEngine](https://github.com/CMTA/RuleEngine) pattern, +the restricted functions do not hardcode a role check. They carry a **modifier** +(`onlyDocumentManager` / `onlyBoundToken`) that delegates to an **overridable +`internal virtual` authorization hook**: + +```solidity +function _authorizeDocumentManagement() internal view virtual { + _checkRole(DOCUMENT_MANAGER_ROLE); +} + +function _authorizeBoundTokenDocumentManagement() internal view virtual { + _checkRole(TOKEN_CONTRACT_ROLE); +} +``` + +This separates the document-management implementation from the authorization +logic: a subclass can override a hook to change *who* is authorized (e.g. a +different role, an allowlist, or open access) without touching the management +functions. The default behavior is the role checks shown above. + ### Events On every write, the engine emits the standard `IERC1643` events **and** the @@ -112,14 +134,14 @@ reads/writes are then forwarded to the engine keyed by the token address. | | | | | | | **DocumentEngine** | Implementation | IERC1643, DocumentEngineInvariant, AccessControl, ERC2771Context | | | | └ | | Public ❗️ | 🛑 | NO❗️ | -| └ | setDocument | Public ❗️ | 🛑 | onlyRole (DOCUMENT_MANAGER_ROLE) | -| └ | removeDocument | External ❗️ | 🛑 | onlyRole (DOCUMENT_MANAGER_ROLE) | -| └ | setDocument | External ❗️ | 🛑 | onlyRole (TOKEN_CONTRACT_ROLE) | -| └ | removeDocument | External ❗️ | 🛑 | onlyRole (TOKEN_CONTRACT_ROLE) | -| └ | batchSetDocuments | External ❗️ | 🛑 | onlyRole | -| └ | batchSetDocuments | External ❗️ | 🛑 | onlyRole | -| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyRole | -| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyRole | +| └ | setDocument | Public ❗️ | 🛑 | onlyDocumentManager | +| └ | removeDocument | External ❗️ | 🛑 | onlyDocumentManager | +| └ | setDocument | External ❗️ | 🛑 | onlyBoundToken | +| └ | removeDocument | External ❗️ | 🛑 | onlyBoundToken | +| └ | batchSetDocuments | External ❗️ | 🛑 | onlyDocumentManager | +| └ | batchSetDocuments | External ❗️ | 🛑 | onlyDocumentManager | +| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyDocumentManager | +| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyDocumentManager | | └ | getDocument | External ❗️ | | NO❗️ | | └ | getDocument | External ❗️ | | NO❗️ | | └ | getAllDocuments | External ❗️ | | NO❗️ | diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index 2d18acd..5f37da7 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -48,7 +48,7 @@ contract DocumentEngine is bytes32 name_, string memory uri_, bytes32 documentHash_ - ) public onlyRole(DOCUMENT_MANAGER_ROLE) { + ) public onlyDocumentManager { _setDocument(smartContract, name_, uri_, documentHash_); } @@ -58,7 +58,7 @@ contract DocumentEngine is function removeDocument( address smartContract, bytes32 name_ - ) external onlyRole(DOCUMENT_MANAGER_ROLE) { + ) external onlyDocumentManager { _removeDocument(smartContract, name_); } @@ -75,7 +75,7 @@ contract DocumentEngine is bytes32 name_, string calldata uri_, bytes32 documentHash_ - ) external override onlyRole(TOKEN_CONTRACT_ROLE) { + ) external override onlyBoundToken { _setDocument(_msgSender(), name_, uri_, documentHash_); } @@ -85,7 +85,7 @@ contract DocumentEngine is */ function removeDocument( bytes32 name_ - ) external override onlyRole(TOKEN_CONTRACT_ROLE) { + ) external override onlyBoundToken { _removeDocument(_msgSender(), name_); } @@ -97,7 +97,7 @@ contract DocumentEngine is bytes32[] calldata names, string[] calldata uris, bytes32[] calldata hashes - ) external onlyRole(DOCUMENT_MANAGER_ROLE) { + ) external onlyDocumentManager { if ( smartContracts.length == 0 || smartContracts.length != names.length || @@ -119,7 +119,7 @@ contract DocumentEngine is bytes32[] calldata names, string[] calldata uris, bytes32[] calldata hashes - ) external onlyRole(DOCUMENT_MANAGER_ROLE) { + ) external onlyDocumentManager { if ( names.length == 0 || names.length != uris.length || @@ -138,7 +138,7 @@ contract DocumentEngine is function batchRemoveDocuments( address[] calldata smartContracts, bytes32[] calldata names - ) external onlyRole(DOCUMENT_MANAGER_ROLE) { + ) external onlyDocumentManager { if ( smartContracts.length == 0 || (smartContracts.length != names.length) @@ -157,7 +157,7 @@ contract DocumentEngine is function batchRemoveDocuments( address smartContract, bytes32[] calldata names - ) external onlyRole(DOCUMENT_MANAGER_ROLE) { + ) external onlyDocumentManager { if (names.length == 0) { revert InvalidInputLength(); } @@ -207,7 +207,53 @@ contract DocumentEngine is return _documentNames[smartContract]; } - /* ============ ACCESS CONTROL ============ */ + /*////////////////////////////////////////////////////////////// + ACCESS CONTROL (flexible) + //////////////////////////////////////////////////////////////*/ + + /** + * @dev Restricts a function to accounts allowed to manage documents on + * behalf of any smart contract (admin path). + * + * The access-control logic is delegated to {_authorizeDocumentManagement}, + * so the document-management implementation is kept separate from the + * authorization: a subclass can override the hook to change *who* is + * authorized without touching the management functions + * (pattern borrowed from CMTAT and CMTA/RuleEngine). + */ + modifier onlyDocumentManager() { + _authorizeDocumentManagement(); + _; + } + + /** + * @dev Restricts a function to tokens bound to this engine, letting them + * manage their own documents (bound-token path). Delegates to + * {_authorizeBoundTokenDocumentManagement}. + */ + modifier onlyBoundToken() { + _authorizeBoundTokenDocumentManagement(); + _; + } + + /** + * @dev Authorization hook for the admin document-management path. + * Default: the caller must hold `DOCUMENT_MANAGER_ROLE`. + * Override to customize the authorization logic. + */ + function _authorizeDocumentManagement() internal view virtual { + _checkRole(DOCUMENT_MANAGER_ROLE); + } + + /** + * @dev Authorization hook for the bound-token document-management path. + * Default: the caller must hold `TOKEN_CONTRACT_ROLE` (the RuleEngine + * binding pattern). Override to customize the authorization logic. + */ + function _authorizeBoundTokenDocumentManagement() internal view virtual { + _checkRole(TOKEN_CONTRACT_ROLE); + } + /* * @dev Returns `true` if `account` has been granted `role`. */ diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index abed6b5..7104913 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -21,6 +21,22 @@ contract CMTATDocumentEngineMock is DocumentEngineModule { function _authorizeDocumentManagement() internal override {} } +/** + * @dev Demonstrates the flexible access control: overriding the authorization + * hook opens the admin document-management path to anyone, without touching the + * document-management implementation. + */ +contract OpenDocumentEngine is DocumentEngine { + constructor( + address admin, + address forwarder + ) DocumentEngine(admin, forwarder) {} + + function _authorizeDocumentManagement() internal view override { + // no access restriction (custom authorization) + } +} + contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { DocumentEngine public documentEngine; address public admin = address(0x1); @@ -262,6 +278,34 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentEngine.removeDocument(selfName); } + /*////////////////////////////////////////////////////////////// + Flexible access control (overridable authorization hook) + //////////////////////////////////////////////////////////////*/ + + function testFlexibleAuthorizationCanBeOverridden() public { + OpenDocumentEngine openEngine = new OpenDocumentEngine( + admin, + AddressZero + ); + + // attacker holds no role, yet can manage documents because the + // authorization hook was overridden to allow anyone. + vm.prank(attacker); + openEngine.setDocument( + testContract, + documentName, + documentURI, + documentHash + ); + + IERC1643.Document memory doc = openEngine.getDocument( + testContract, + documentName + ); + assertEq(doc.uri, documentURI); + assertEq(doc.documentHash, documentHash); + } + /*////////////////////////////////////////////////////////////// Set documents //////////////////////////////////////////////////////////////*/ From 19cdb8ff3934c680454df2e22b073947179245b7 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 07:20:34 +0200 Subject: [PATCH 03/47] refactor: split DocumentEngine into DocumentEngineBase (logic) + DocumentEngine (access control + deployment) --- AGENTS.md | 16 +- CHANGELOG.md | 1 + CLAUDE.md | 16 +- README.md | 18 +- doc/ERCSpecification/erc-8303-draft.md | 131 +++++++++++ src/DocumentEngine.sol | 308 ++---------------------- src/DocumentEngineBase.sol | 311 +++++++++++++++++++++++++ 7 files changed, 505 insertions(+), 296 deletions(-) create mode 100644 doc/ERCSpecification/erc-8303-draft.md create mode 100644 src/DocumentEngineBase.sol diff --git a/AGENTS.md b/AGENTS.md index ca26a28..1791154 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -49,17 +49,27 @@ addressed by a `bytes32` name. ``` src/ -├── DocumentEngine.sol # Main contract: ERC-1643 impl, both management -│ # paths, batch functions, ERC-2771, access control +├── DocumentEngineBase.sol # Abstract base: ERC-1643 document logic + storage, +│ # both management paths, batch functions, modifiers, +│ # and the ABSTRACT _authorize* hooks (no access control) +├── DocumentEngine.sol # Deployment contract: defines the ACCESS CONTROL +│ # (AccessControl, _authorize* impls, hasRole) + ERC-2771, +│ # VERSION, constructor └── DocumentEngineInvariant.sol # Errors, roles (DOCUMENT_MANAGER_ROLE, # TOKEN_CONTRACT_ROLE) and the optional multi-token events test/ └── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin path, # bound-token path, batch ops, CMTAT integration - # (CMTATDocumentEngineMock built on DocumentEngineModule) + # (CMTATDocumentEngineMock built on DocumentEngineModule), + # flexible-authorization override (OpenDocumentEngine) ``` +**Contract split (CMTAT module/deployment pattern):** `DocumentEngineBase` holds +the document logic and abstract `_authorize*` hooks; `DocumentEngine` is the +deployable contract that supplies the concrete access control. Add new +management logic in the base; change *who* is authorized in the deployment. + Other important files: - `foundry.toml` — solc `0.8.34`, `evm_version = prague` (required by CMTAT v3). diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ee30c9..f1b5a72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) - **Bound-token document management (RuleEngine binding pattern)**: implement the now-mandatory `IERC1643.setDocument(name, uri, hash)` and `removeDocument(name)`. They are gated by a new `TOKEN_CONTRACT_ROLE` and scoped to the caller (`_msgSender()`) own namespace. A token bound with `grantRole(TOKEN_CONTRACT_ROLE, token)` manages its own documents and can never affect another contract's documents. The existing admin overloads (explicit `address`, `DOCUMENT_MANAGER_ROLE`) are unchanged, so both systems work side by side. - **Optional multi-token events**: alongside the standard `IERC1643` events, the engine now also emits `DocumentUpdatedForContract` / `DocumentRemovedForContract`, which carry the `smartContract` (token) address so off-chain indexers can tell which contract a document belongs to during multi-contract operations. See [`ERC-1643-proposition.md`](./ERC-1643-proposition.md) for the proposed optional standard extension. - **Flexible access control (CMTAT / RuleEngine pattern)**: the restricted functions now use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to overridable `internal virtual` authorization hooks `_authorizeDocumentManagement()` / `_authorizeBoundTokenDocumentManagement()` (default: `DOCUMENT_MANAGER_ROLE` / `TOKEN_CONTRACT_ROLE`). This separates the document-management implementation from the authorization logic, so a subclass can change *who* is authorized without touching the management functions. Default behavior is unchanged. +- **Split into a base contract and a deployment contract** (CMTAT module/deployment pattern): the document-management logic and storage now live in the new abstract `DocumentEngineBase` (with abstract `_authorize*` hooks), while `DocumentEngine` is the deployment contract that defines the access control (`AccessControl`, the concrete hooks and `hasRole`) and the ERC-2771 wiring. The deployable `DocumentEngine` API and behavior are unchanged. ### Notes / bottlenecks diff --git a/CLAUDE.md b/CLAUDE.md index ca26a28..1791154 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -49,17 +49,27 @@ addressed by a `bytes32` name. ``` src/ -├── DocumentEngine.sol # Main contract: ERC-1643 impl, both management -│ # paths, batch functions, ERC-2771, access control +├── DocumentEngineBase.sol # Abstract base: ERC-1643 document logic + storage, +│ # both management paths, batch functions, modifiers, +│ # and the ABSTRACT _authorize* hooks (no access control) +├── DocumentEngine.sol # Deployment contract: defines the ACCESS CONTROL +│ # (AccessControl, _authorize* impls, hasRole) + ERC-2771, +│ # VERSION, constructor └── DocumentEngineInvariant.sol # Errors, roles (DOCUMENT_MANAGER_ROLE, # TOKEN_CONTRACT_ROLE) and the optional multi-token events test/ └── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin path, # bound-token path, batch ops, CMTAT integration - # (CMTATDocumentEngineMock built on DocumentEngineModule) + # (CMTATDocumentEngineMock built on DocumentEngineModule), + # flexible-authorization override (OpenDocumentEngine) ``` +**Contract split (CMTAT module/deployment pattern):** `DocumentEngineBase` holds +the document logic and abstract `_authorize*` hooks; `DocumentEngine` is the +deployable contract that supplies the concrete access control. Add new +management logic in the base; change *who* is authorized in the deployment. + Other important files: - `foundry.toml` — solc `0.8.34`, `evm_version = prague` (required by CMTAT v3). diff --git a/README.md b/README.md index 9b8e488..28b9272 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,22 @@ reads/writes are then forwarded to the engine keyed by the token address. +## Architecture + +The engine is split into two contracts (CMTAT module/deployment pattern): + +- **`DocumentEngineBase`** (abstract) — holds the document storage and all the + ERC-1643 document-management functions, plus the `onlyDocumentManager` / + `onlyBoundToken` modifiers and the **abstract** `_authorize*` hooks. It is + agnostic to the access-control implementation. +- **`DocumentEngine`** (deployment) — the concrete, deployable contract. It + defines the **access control** (`AccessControl`, the `_authorize*` hook + implementations and the `hasRole` override) and wires the ERC-2771 (gasless) + support. + +`DocumentEngineInvariant` provides the shared errors, roles +(`DOCUMENT_MANAGER_ROLE`, `TOKEN_CONTRACT_ROLE`) and the optional multi-token events. + ## Schema ### Inheritance @@ -132,7 +148,7 @@ reads/writes are then forwarded to the engine keyed by the token address. | :----------------: | :------------------: | :----------------------------------------------: | :------------: | :-----------: | | └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | | | | | | | -| **DocumentEngine** | Implementation | IERC1643, DocumentEngineInvariant, AccessControl, ERC2771Context | | | +| **DocumentEngine** | Implementation | DocumentEngineBase, AccessControl, ERC2771Context | | | | └ | | Public ❗️ | 🛑 | NO❗️ | | └ | setDocument | Public ❗️ | 🛑 | onlyDocumentManager | | └ | removeDocument | External ❗️ | 🛑 | onlyDocumentManager | diff --git a/doc/ERCSpecification/erc-8303-draft.md b/doc/ERCSpecification/erc-8303-draft.md new file mode 100644 index 0000000..15be396 --- /dev/null +++ b/doc/ERCSpecification/erc-8303-draft.md @@ -0,0 +1,131 @@ +--- +eip: 8303 +title: Contract Version +description: Interface for exposing a contract implementation version string +author: Ryan Sauge (@rya-sge) +discussions-to: https://ethereum-magicians.org/t/erc-8303-contract-version/28795 +status: Draft +type: Standards Track +category: ERC +created: 2026-02-12 +--- + +## Abstract + +This ERC defines a minimal interface to expose a contract version string through a standardized `version()` view function. The design is based on the version pattern used by [ERC-3643](./eip-3643.md), while remaining token-agnostic and applicable to other smart contract domains, including DeFi applications such as lending protocols. + +## Motivation + +Integrators frequently need a simple, on-chain way to identify which contract implementation they interact with. A standardized version function improves: + +- integration safety (feature gating by version), +- operations (faster incident triage), +- governance and migration tracking (upgrade visibility), +- ecosystem tooling interoperability. + +It is also useful for end-users, developers, and security auditors to identify which version of a codebase is currently used by a deployed contract. + +The same requirement appears in permissioned token systems ([ERC-3643](./eip-3643.md)) and in DeFi systems where contracts evolve over time. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174). + +### Interface + +```solidity +interface IERC8303 { + /// @notice Returns the implementation version string. + /// @return The version value (for example "1.0.0"). + function version() external view returns (string memory); +} +``` + +### Required Behavior + +1. **Version read** + - `version()` MUST be a view function. + - `version()` MUST NOT revert under normal operation. + - `version()` MUST return a non-empty string. + +2. **Version meaning** + - Returned values SHOULD be stable and machine-comparable by off-chain tooling. + - Returned values SHOULD follow a Semantic Versioning 2.0.0-like format: `MAJOR.MINOR.PATCH` using decimal integers (for example `1.0.0`, `3.2.1`). + - The canonical recommended pattern is `^[0-9]+\.[0-9]+\.[0-9]+$`. + - Implementations MAY define their own versioning policy, but SHOULD document it publicly. + +3. **Deployment model compatibility** + - This interface is compatible with immutable deployments and proxy-based upgradeable deployments. + - In upgradeable systems, `version()` SHOULD reflect the active implementation seen by users and integrators. + +### [ERC-165](./eip-165.md) + +Implementations SHOULD support [ERC-165](./eip-165.md) interface discovery for this interface. + +If an implementation supports [ERC-165](./eip-165.md), `supportsInterface(type(IERC8303).interfaceId)` MUST return `true`. + +- The interface id for `IERC8303` is `0x54fd4d50`. + +### Compatibility Note for ERC-3643 Integrations + +Integrators MAY treat legacy ERC-3643 token contracts exposing a compatible `version()` function as implementing this ERC even if they do not advertise ERC-165 support. + +## Rationale + +- **Minimal scope**: A single function maximizes adoption and keeps gas/runtime complexity negligible. +- **ERC-3643 alignment**: Reuses a proven pattern already used in regulated token implementations. +- **Token-agnostic design**: The interface applies to token contracts and non-token contracts alike. +- **Optional ERC-165**: ERC-165 support is recommended but not required, lowering the adoption barrier for contracts that do not implement interface discovery. When ERC-165 is supported, advertising this interface is mandatory to ensure consistent detection by integrators. +- **`string` over `bytes32`**: A human-readable string is preferred to a fixed-size bytes32 for legibility in explorers and tooling, at the cost of marginally higher gas for the return value. + +## Backwards Compatibility + +This ERC is fully additive. Contracts already exposing `version()` are naturally compatible if they match the interface signature. + +## Test Cases + +The following test cases apply to any conforming implementation. + +1. `version()` MUST NOT revert. +2. `version()` MUST return a non-empty string. +3. `version()` MUST return the version string declared by the implementation (e.g. `"1.0.0"`). +4. If the contract supports [ERC-165](./eip-165.md), `supportsInterface(0x54fd4d50)` MUST return `true`. +5. If the contract supports [ERC-165](./eip-165.md), `supportsInterface(0xffffffff)` MUST return `false`. + +## Reference Implementation + +Reference implementations are provided in the assets folder: the [interface](../assets/erc-8303/src/IERC8303.sol) and a [base implementation](../assets/erc-8303/src/ERC8303.sol), along with usage examples for [ERC-20](../assets/erc-8303/src/examples/ERC20VersionedExample.sol) and [ERC-721](../assets/erc-8303/src/examples/ERC721VersionedExample.sol) tokens. These examples are provided for educational purposes only and are not audited. + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "./IERC8303.sol"; +import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; + +contract ERC8303Example is IERC8303, ERC165 { + function version() external pure override returns (string memory) { + return "1.0.0"; + } + + function supportsInterface(bytes4 interfaceId) + public + view + override + returns (bool) + { + return interfaceId == type(IERC8303).interfaceId + || super.supportsInterface(interfaceId); + } +} +``` + +## Security Considerations + +- `version()` is metadata and must not be used as a sole authorization primitive. +- In upgradeable systems, governance controls remain the trust anchor; version reporting does not prevent malicious upgrades. +- Integrators should combine version checks with other trust signals (governance model, audits, deployment provenance). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index 5f37da7..06bab95 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -3,27 +3,23 @@ pragma solidity ^0.8.20; import "OZ/access/AccessControl.sol"; import "OZ/metatx/ERC2771Context.sol"; -import "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; -import "./DocumentEngineInvariant.sol"; +import "./DocumentEngineBase.sol"; /** * @title DocumentEngine - * @notice contract to manage documents on-chain through ERC-1643 + * @notice Deployment contract to manage documents on-chain through ERC-1643. + * @dev Wires the document-management logic ({DocumentEngineBase}) with a + * concrete access-control implementation. The authorization hooks are defined + * here (role-based `AccessControl`), keeping the access control separate from + * the document-management logic (CMTAT / CMTA-RuleEngine pattern). It also wires + * the ERC-2771 (gasless) meta-transaction support. */ -contract DocumentEngine is - IERC1643, - DocumentEngineInvariant, - AccessControl, - ERC2771Context -{ +contract DocumentEngine is DocumentEngineBase, AccessControl, ERC2771Context { /** * @notice * Get the current version of the smart contract */ string public constant VERSION = "0.4.0"; - // Mapping from contract addresses to document names to their corresponding Document structs - mapping(address => mapping(bytes32 => Document)) private _documents; - mapping(address => bytes32[]) private _documentNames; // Constructor to initialize the admin role constructor( @@ -37,220 +33,28 @@ contract DocumentEngine is } /*////////////////////////////////////////////////////////////// - PUBLIC/EXTERNAL FUNCTIONS + ACCESS CONTROL (implementation) //////////////////////////////////////////////////////////////*/ /** - * @notice Restricted function to set or update a document + * @dev Authorization for the admin document-management path. + * The caller must hold `DOCUMENT_MANAGER_ROLE`. Override to customize. */ - function setDocument( - address smartContract, - bytes32 name_, - string memory uri_, - bytes32 documentHash_ - ) public onlyDocumentManager { - _setDocument(smartContract, name_, uri_, documentHash_); - } - - /** - * @notice Restricted function to remove a document for a given smart contract and name - */ - function removeDocument( - address smartContract, - bytes32 name_ - ) external onlyDocumentManager { - _removeDocument(smartContract, name_); - } - - /* ============ ERC-1643 (bound token) ============ */ - - /** - * @notice ERC-1643 function to set or update a document for the caller. - * @dev The document is stored under the caller (`_msgSender()`) namespace. - * The caller must be a token bound to this engine (`TOKEN_CONTRACT_ROLE`), - * following the RuleEngine binding pattern. A bound token can only manage - * its own documents; it can never affect another contract's documents. - */ - function setDocument( - bytes32 name_, - string calldata uri_, - bytes32 documentHash_ - ) external override onlyBoundToken { - _setDocument(_msgSender(), name_, uri_, documentHash_); - } - - /** - * @notice ERC-1643 function to remove a document for the caller. - * @dev See {setDocument}. Scoped to the caller (`_msgSender()`) namespace. - */ - function removeDocument( - bytes32 name_ - ) external override onlyBoundToken { - _removeDocument(_msgSender(), name_); - } - - /** - * @notice Batch version of setDocument to handle multiple documents at once - */ - function batchSetDocuments( - address[] calldata smartContracts, - bytes32[] calldata names, - string[] calldata uris, - bytes32[] calldata hashes - ) external onlyDocumentManager { - if ( - smartContracts.length == 0 || - smartContracts.length != names.length || - names.length != uris.length || - uris.length != hashes.length - ) { - revert InvalidInputLength(); - } - for (uint256 i = 0; i < smartContracts.length; i++) { - _setDocument(smartContracts[i], names[i], uris[i], hashes[i]); - } - } - - /** - * @notice Batch version of setDocument to handle multiple documents at once - */ - function batchSetDocuments( - address smartContract, - bytes32[] calldata names, - string[] calldata uris, - bytes32[] calldata hashes - ) external onlyDocumentManager { - if ( - names.length == 0 || - names.length != uris.length || - uris.length != hashes.length - ) { - revert InvalidInputLength(); - } - for (uint256 i = 0; i < names.length; ++i) { - _setDocument(smartContract, names[i], uris[i], hashes[i]); - } - } - - /** - * @notice Batch version of removeDocument to handle multiple documents at once - */ - function batchRemoveDocuments( - address[] calldata smartContracts, - bytes32[] calldata names - ) external onlyDocumentManager { - if ( - smartContracts.length == 0 || - (smartContracts.length != names.length) - ) { - revert InvalidInputLength(); - } - - for (uint256 i = 0; i < smartContracts.length; ++i) { - _removeDocument(smartContracts[i], names[i]); - } - } - - /** - * @notice Batch version of removeDocument to handle multiple documents at once - */ - function batchRemoveDocuments( - address smartContract, - bytes32[] calldata names - ) external onlyDocumentManager { - if (names.length == 0) { - revert InvalidInputLength(); - } - - for (uint256 i = 0; i < names.length; ++i) { - _removeDocument(smartContract, names[i]); - } - } - - /** - * @notice ERC-1643 function to get a document for the caller (`_msgSender()`) - */ - function getDocument( - bytes32 name_ - ) external view override returns (Document memory) { - return _getDocument(_msgSender(), name_); - } - - /** - * @notice Public function to get a document for a specific contract address - */ - function getDocument( - address smartContract, - bytes32 name_ - ) external view returns (Document memory) { - return _getDocument(smartContract, name_); + function _authorizeDocumentManagement() internal view virtual override { + _checkRole(DOCUMENT_MANAGER_ROLE); } /** - * @notice Get all document names for msg.sender + * @dev Authorization for the bound-token document-management path. + * The caller must hold `TOKEN_CONTRACT_ROLE` (the RuleEngine binding + * pattern). Override to customize. */ - function getAllDocuments() - external + function _authorizeBoundTokenDocumentManagement() + internal view + virtual override - returns (bytes32[] memory) { - return _documentNames[_msgSender()]; - } - - /** - * @notice Get all document names for a specific smart contract - */ - function getAllDocuments( - address smartContract - ) external view returns (bytes32[] memory) { - return _documentNames[smartContract]; - } - - /*////////////////////////////////////////////////////////////// - ACCESS CONTROL (flexible) - //////////////////////////////////////////////////////////////*/ - - /** - * @dev Restricts a function to accounts allowed to manage documents on - * behalf of any smart contract (admin path). - * - * The access-control logic is delegated to {_authorizeDocumentManagement}, - * so the document-management implementation is kept separate from the - * authorization: a subclass can override the hook to change *who* is - * authorized without touching the management functions - * (pattern borrowed from CMTAT and CMTA/RuleEngine). - */ - modifier onlyDocumentManager() { - _authorizeDocumentManagement(); - _; - } - - /** - * @dev Restricts a function to tokens bound to this engine, letting them - * manage their own documents (bound-token path). Delegates to - * {_authorizeBoundTokenDocumentManagement}. - */ - modifier onlyBoundToken() { - _authorizeBoundTokenDocumentManagement(); - _; - } - - /** - * @dev Authorization hook for the admin document-management path. - * Default: the caller must hold `DOCUMENT_MANAGER_ROLE`. - * Override to customize the authorization logic. - */ - function _authorizeDocumentManagement() internal view virtual { - _checkRole(DOCUMENT_MANAGER_ROLE); - } - - /** - * @dev Authorization hook for the bound-token document-management path. - * Default: the caller must hold `TOKEN_CONTRACT_ROLE` (the RuleEngine - * binding pattern). Override to customize the authorization logic. - */ - function _authorizeBoundTokenDocumentManagement() internal view virtual { _checkRole(TOKEN_CONTRACT_ROLE); } @@ -268,80 +72,6 @@ contract DocumentEngine is return AccessControl.hasRole(role, account); } - /*////////////////////////////////////////////////////////////// - INTERNAL FUNCTIONS - //////////////////////////////////////////////////////////////*/ - - /** - * @dev Internal function to fetch a document - */ - function _getDocument( - address smartContract, - bytes32 name_ - ) internal view returns (Document memory) { - return _documents[smartContract][name_]; - } - - /** - * @dev Internal helper to remove the document name from the list of document names - */ - function _removeDocumentName( - address smartContract, - bytes32 name_ - ) internal { - uint256 length = _documentNames[smartContract].length; - for (uint256 i = 0; i < length; ++i) { - if (_documentNames[smartContract][i] == name_) { - _documentNames[smartContract][i] = _documentNames[ - smartContract - ][length - 1]; - _documentNames[smartContract].pop(); - break; - } - } - } - - function _removeDocument(address smartContract, bytes32 name_) internal { - Document memory doc = _documents[smartContract][name_]; - // Standard ERC-1643 event - emit DocumentRemoved(name_, doc.uri, doc.documentHash); - // Optional multi-token event (see ERC-1643-proposition.md) - emit DocumentRemovedForContract( - smartContract, - name_, - doc.uri, - doc.documentHash - ); - - delete _documents[smartContract][name_]; - _removeDocumentName(smartContract, name_); - } - - function _setDocument( - address smartContract, - bytes32 name_, - string memory uri_, - bytes32 documentHash_ - ) internal { - Document storage doc = _documents[smartContract][name_]; - if (doc.lastModified == 0) { - // new document - _documentNames[smartContract].push(name_); - } - doc.uri = uri_; - doc.documentHash = documentHash_; - doc.lastModified = block.timestamp; - // Standard ERC-1643 event - emit DocumentUpdated(name_, uri_, documentHash_); - // Optional multi-token event (see ERC-1643-proposition.md) - emit DocumentUpdatedForContract( - smartContract, - name_, - uri_, - documentHash_ - ); - } - /*////////////////////////////////////////////////////////////// ERC2771 //////////////////////////////////////////////////////////////*/ diff --git a/src/DocumentEngineBase.sol b/src/DocumentEngineBase.sol new file mode 100644 index 0000000..0e61b4c --- /dev/null +++ b/src/DocumentEngineBase.sol @@ -0,0 +1,311 @@ +//SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.20; + +import "OZ/utils/Context.sol"; +import "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; +import "./DocumentEngineInvariant.sol"; + +/** + * @title DocumentEngineBase + * @notice Document management logic (ERC-1643) for several smart contracts. + * @dev This abstract base holds the document storage and all the + * document-management functions, but it is **agnostic to the access-control + * implementation**. Authorization is delegated to the abstract hooks + * {_authorizeDocumentManagement} and {_authorizeBoundTokenDocumentManagement} + * (through the `onlyDocumentManager` / `onlyBoundToken` modifiers), which a + * deployment contract must implement (see {DocumentEngine}). + * + * This separation (base logic + deployment-defined access control) follows the + * CMTAT and CMTA/RuleEngine pattern. + */ +abstract contract DocumentEngineBase is + IERC1643, + DocumentEngineInvariant, + Context +{ + // Mapping from contract addresses to document names to their corresponding Document structs + mapping(address => mapping(bytes32 => Document)) private _documents; + mapping(address => bytes32[]) private _documentNames; + + /*////////////////////////////////////////////////////////////// + ACCESS CONTROL (hooks) + //////////////////////////////////////////////////////////////*/ + + /** + * @dev Restricts a function to accounts allowed to manage documents on + * behalf of any smart contract (admin path). Delegates the authorization + * to {_authorizeDocumentManagement} so that the document-management + * implementation stays separate from the access-control logic. + */ + modifier onlyDocumentManager() { + _authorizeDocumentManagement(); + _; + } + + /** + * @dev Restricts a function to tokens bound to this engine, letting them + * manage their own documents (bound-token path). Delegates to + * {_authorizeBoundTokenDocumentManagement}. + */ + modifier onlyBoundToken() { + _authorizeBoundTokenDocumentManagement(); + _; + } + + /** + * @dev Authorization hook for the admin document-management path. + * Implemented by the deployment contract (e.g. a role check). + */ + function _authorizeDocumentManagement() internal view virtual; + + /** + * @dev Authorization hook for the bound-token document-management path. + * Implemented by the deployment contract (e.g. a role check). + */ + function _authorizeBoundTokenDocumentManagement() internal view virtual; + + /*////////////////////////////////////////////////////////////// + PUBLIC/EXTERNAL FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Restricted function to set or update a document + */ + function setDocument( + address smartContract, + bytes32 name_, + string memory uri_, + bytes32 documentHash_ + ) public onlyDocumentManager { + _setDocument(smartContract, name_, uri_, documentHash_); + } + + /** + * @notice Restricted function to remove a document for a given smart contract and name + */ + function removeDocument( + address smartContract, + bytes32 name_ + ) external onlyDocumentManager { + _removeDocument(smartContract, name_); + } + + /* ============ ERC-1643 (bound token) ============ */ + + /** + * @notice ERC-1643 function to set or update a document for the caller. + * @dev The document is stored under the caller (`_msgSender()`) namespace. + * The caller must be a token bound to this engine (`TOKEN_CONTRACT_ROLE`), + * following the RuleEngine binding pattern. A bound token can only manage + * its own documents; it can never affect another contract's documents. + */ + function setDocument( + bytes32 name_, + string calldata uri_, + bytes32 documentHash_ + ) external override onlyBoundToken { + _setDocument(_msgSender(), name_, uri_, documentHash_); + } + + /** + * @notice ERC-1643 function to remove a document for the caller. + * @dev See {setDocument}. Scoped to the caller (`_msgSender()`) namespace. + */ + function removeDocument( + bytes32 name_ + ) external override onlyBoundToken { + _removeDocument(_msgSender(), name_); + } + + /** + * @notice Batch version of setDocument to handle multiple documents at once + */ + function batchSetDocuments( + address[] calldata smartContracts, + bytes32[] calldata names, + string[] calldata uris, + bytes32[] calldata hashes + ) external onlyDocumentManager { + if ( + smartContracts.length == 0 || + smartContracts.length != names.length || + names.length != uris.length || + uris.length != hashes.length + ) { + revert InvalidInputLength(); + } + for (uint256 i = 0; i < smartContracts.length; i++) { + _setDocument(smartContracts[i], names[i], uris[i], hashes[i]); + } + } + + /** + * @notice Batch version of setDocument to handle multiple documents at once + */ + function batchSetDocuments( + address smartContract, + bytes32[] calldata names, + string[] calldata uris, + bytes32[] calldata hashes + ) external onlyDocumentManager { + if ( + names.length == 0 || + names.length != uris.length || + uris.length != hashes.length + ) { + revert InvalidInputLength(); + } + for (uint256 i = 0; i < names.length; ++i) { + _setDocument(smartContract, names[i], uris[i], hashes[i]); + } + } + + /** + * @notice Batch version of removeDocument to handle multiple documents at once + */ + function batchRemoveDocuments( + address[] calldata smartContracts, + bytes32[] calldata names + ) external onlyDocumentManager { + if ( + smartContracts.length == 0 || + (smartContracts.length != names.length) + ) { + revert InvalidInputLength(); + } + + for (uint256 i = 0; i < smartContracts.length; ++i) { + _removeDocument(smartContracts[i], names[i]); + } + } + + /** + * @notice Batch version of removeDocument to handle multiple documents at once + */ + function batchRemoveDocuments( + address smartContract, + bytes32[] calldata names + ) external onlyDocumentManager { + if (names.length == 0) { + revert InvalidInputLength(); + } + + for (uint256 i = 0; i < names.length; ++i) { + _removeDocument(smartContract, names[i]); + } + } + + /** + * @notice ERC-1643 function to get a document for the caller (`_msgSender()`) + */ + function getDocument( + bytes32 name_ + ) external view override returns (Document memory) { + return _getDocument(_msgSender(), name_); + } + + /** + * @notice Public function to get a document for a specific contract address + */ + function getDocument( + address smartContract, + bytes32 name_ + ) external view returns (Document memory) { + return _getDocument(smartContract, name_); + } + + /** + * @notice Get all document names for msg.sender + */ + function getAllDocuments() + external + view + override + returns (bytes32[] memory) + { + return _documentNames[_msgSender()]; + } + + /** + * @notice Get all document names for a specific smart contract + */ + function getAllDocuments( + address smartContract + ) external view returns (bytes32[] memory) { + return _documentNames[smartContract]; + } + + /*////////////////////////////////////////////////////////////// + INTERNAL FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + /** + * @dev Internal function to fetch a document + */ + function _getDocument( + address smartContract, + bytes32 name_ + ) internal view returns (Document memory) { + return _documents[smartContract][name_]; + } + + /** + * @dev Internal helper to remove the document name from the list of document names + */ + function _removeDocumentName( + address smartContract, + bytes32 name_ + ) internal { + uint256 length = _documentNames[smartContract].length; + for (uint256 i = 0; i < length; ++i) { + if (_documentNames[smartContract][i] == name_) { + _documentNames[smartContract][i] = _documentNames[ + smartContract + ][length - 1]; + _documentNames[smartContract].pop(); + break; + } + } + } + + function _removeDocument(address smartContract, bytes32 name_) internal { + Document memory doc = _documents[smartContract][name_]; + // Standard ERC-1643 event + emit DocumentRemoved(name_, doc.uri, doc.documentHash); + // Optional multi-token event (see ERC-1643-proposition.md) + emit DocumentRemovedForContract( + smartContract, + name_, + doc.uri, + doc.documentHash + ); + + delete _documents[smartContract][name_]; + _removeDocumentName(smartContract, name_); + } + + function _setDocument( + address smartContract, + bytes32 name_, + string memory uri_, + bytes32 documentHash_ + ) internal { + Document storage doc = _documents[smartContract][name_]; + if (doc.lastModified == 0) { + // new document + _documentNames[smartContract].push(name_); + } + doc.uri = uri_; + doc.documentHash = documentHash_; + doc.lastModified = block.timestamp; + // Standard ERC-1643 event + emit DocumentUpdated(name_, uri_, documentHash_); + // Optional multi-token event (see ERC-1643-proposition.md) + emit DocumentUpdatedForContract( + smartContract, + name_, + uri_, + documentHash_ + ); + } +} From f544ce5f3df2ae48bff8bfd05494b45f07c6526c Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 07:56:12 +0200 Subject: [PATCH 04/47] feat: add VersionModule implementing ERC-8303 (version() + ERC-165) --- AGENTS.md | 17 ++++++++----- CHANGELOG.md | 1 + CLAUDE.md | 17 ++++++++----- README.md | 25 ++++++++++++++++++- src/DocumentEngine.sol | 27 ++++++++++++++------ src/interfaces/IERC8303.sol | 14 +++++++++++ src/modules/VersionModule.sol | 47 +++++++++++++++++++++++++++++++++++ test/DocumentEngine.t.sol | 35 ++++++++++++++++++++++++++ 8 files changed, 162 insertions(+), 21 deletions(-) create mode 100644 src/interfaces/IERC8303.sol create mode 100644 src/modules/VersionModule.sol diff --git a/AGENTS.md b/AGENTS.md index 1791154..62f2967 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,10 +53,15 @@ src/ │ # both management paths, batch functions, modifiers, │ # and the ABSTRACT _authorize* hooks (no access control) ├── DocumentEngine.sol # Deployment contract: defines the ACCESS CONTROL -│ # (AccessControl, _authorize* impls, hasRole) + ERC-2771, -│ # VERSION, constructor -└── DocumentEngineInvariant.sol # Errors, roles (DOCUMENT_MANAGER_ROLE, - # TOKEN_CONTRACT_ROLE) and the optional multi-token events +│ # (AccessControl, _authorize* impls, hasRole), ERC-2771, +│ # combined supportsInterface, constructor +├── DocumentEngineInvariant.sol # Errors, roles (DOCUMENT_MANAGER_ROLE, +│ # TOKEN_CONTRACT_ROLE) and the optional multi-token events +├── interfaces/ +│ └── IERC8303.sol # ERC-8303 "Contract Version" interface (id 0x54fd4d50) +└── modules/ + └── VersionModule.sol # Version module: implements ERC-8303 version() + ERC-165, + # holds the VERSION constant (currently "0.4.0") test/ └── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin path, @@ -97,8 +102,8 @@ forge test --gas-report ## Conventions -- The contract `VERSION` constant (in `src/DocumentEngine.sol`) must match the - latest `CHANGELOG.md` entry on release. +- The `VERSION` constant (in `src/modules/VersionModule.sol`, exposed via + ERC-8303 `version()`) must match the latest `CHANGELOG.md` entry on release. - Bump `MAJOR` on incompatible proxy-storage / external-library or API changes, `MINOR` for backward-compatible features, `PATCH` for backward-compatible fixes. - A bound token can only ever affect its **own** document namespace — never break diff --git a/CHANGELOG.md b/CHANGELOG.md index f1b5a72..9a6b332 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) - **Optional multi-token events**: alongside the standard `IERC1643` events, the engine now also emits `DocumentUpdatedForContract` / `DocumentRemovedForContract`, which carry the `smartContract` (token) address so off-chain indexers can tell which contract a document belongs to during multi-contract operations. See [`ERC-1643-proposition.md`](./ERC-1643-proposition.md) for the proposed optional standard extension. - **Flexible access control (CMTAT / RuleEngine pattern)**: the restricted functions now use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to overridable `internal virtual` authorization hooks `_authorizeDocumentManagement()` / `_authorizeBoundTokenDocumentManagement()` (default: `DOCUMENT_MANAGER_ROLE` / `TOKEN_CONTRACT_ROLE`). This separates the document-management implementation from the authorization logic, so a subclass can change *who* is authorized without touching the management functions. Default behavior is unchanged. - **Split into a base contract and a deployment contract** (CMTAT module/deployment pattern): the document-management logic and storage now live in the new abstract `DocumentEngineBase` (with abstract `_authorize*` hooks), while `DocumentEngine` is the deployment contract that defines the access control (`AccessControl`, the concrete hooks and `hasRole`) and the ERC-2771 wiring. The deployable `DocumentEngine` API and behavior are unchanged. +- **Version module implementing ERC-8303**: the version is now exposed through a dedicated `VersionModule` (`src/modules/VersionModule.sol`) implementing the `IERC8303` interface (`src/interfaces/IERC8303.sol`). It adds a standard `version()` view function (in addition to the existing public `VERSION` constant) and advertises ERC-8303 via ERC-165 (`supportsInterface(0x54fd4d50) == true`). `DocumentEngine` combines the module's `supportsInterface` with `AccessControl`. ### Notes / bottlenecks diff --git a/CLAUDE.md b/CLAUDE.md index 1791154..62f2967 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -53,10 +53,15 @@ src/ │ # both management paths, batch functions, modifiers, │ # and the ABSTRACT _authorize* hooks (no access control) ├── DocumentEngine.sol # Deployment contract: defines the ACCESS CONTROL -│ # (AccessControl, _authorize* impls, hasRole) + ERC-2771, -│ # VERSION, constructor -└── DocumentEngineInvariant.sol # Errors, roles (DOCUMENT_MANAGER_ROLE, - # TOKEN_CONTRACT_ROLE) and the optional multi-token events +│ # (AccessControl, _authorize* impls, hasRole), ERC-2771, +│ # combined supportsInterface, constructor +├── DocumentEngineInvariant.sol # Errors, roles (DOCUMENT_MANAGER_ROLE, +│ # TOKEN_CONTRACT_ROLE) and the optional multi-token events +├── interfaces/ +│ └── IERC8303.sol # ERC-8303 "Contract Version" interface (id 0x54fd4d50) +└── modules/ + └── VersionModule.sol # Version module: implements ERC-8303 version() + ERC-165, + # holds the VERSION constant (currently "0.4.0") test/ └── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin path, @@ -97,8 +102,8 @@ forge test --gas-report ## Conventions -- The contract `VERSION` constant (in `src/DocumentEngine.sol`) must match the - latest `CHANGELOG.md` entry on release. +- The `VERSION` constant (in `src/modules/VersionModule.sol`, exposed via + ERC-8303 `version()`) must match the latest `CHANGELOG.md` entry on release. - Bump `MAJOR` on incompatible proxy-storage / external-library or API changes, `MINOR` for backward-compatible features, `PATCH` for backward-compatible fixes. - A bound token can only ever affect its **own** document namespace — never break diff --git a/README.md b/README.md index 28b9272..fc70b03 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,29 @@ The engine is split into two contracts (CMTAT module/deployment pattern): `DocumentEngineInvariant` provides the shared errors, roles (`DOCUMENT_MANAGER_ROLE`, `TOKEN_CONTRACT_ROLE`) and the optional multi-token events. +`VersionModule` (`src/modules/VersionModule.sol`) isolates the version concern +and implements [ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) +(see below). + +## Version (ERC-8303) + +The contract version is exposed through the `VersionModule`, which implements +the [ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) +`IERC8303` interface: + +```solidity +interface IERC8303 { + function version() external view returns (string memory); +} +``` + +- `version()` returns the current version string (e.g. `"0.4.0"`), following + Semantic Versioning 2.0.0. +- The public `VERSION` constant is kept for backward compatibility and returns + the same value. +- ERC-165 discovery is supported: `supportsInterface(0x54fd4d50)` (the ERC-8303 + interface id) returns `true`. + ## Schema ### Inheritance @@ -148,7 +171,7 @@ The engine is split into two contracts (CMTAT module/deployment pattern): | :----------------: | :------------------: | :----------------------------------------------: | :------------: | :-----------: | | └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | | | | | | | -| **DocumentEngine** | Implementation | DocumentEngineBase, AccessControl, ERC2771Context | | | +| **DocumentEngine** | Implementation | DocumentEngineBase, VersionModule, AccessControl, ERC2771Context | | | | └ | | Public ❗️ | 🛑 | NO❗️ | | └ | setDocument | Public ❗️ | 🛑 | onlyDocumentManager | | └ | removeDocument | External ❗️ | 🛑 | onlyDocumentManager | diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index 06bab95..e28932e 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.20; import "OZ/access/AccessControl.sol"; import "OZ/metatx/ERC2771Context.sol"; import "./DocumentEngineBase.sol"; +import "./modules/VersionModule.sol"; /** * @title DocumentEngine @@ -11,16 +12,16 @@ import "./DocumentEngineBase.sol"; * @dev Wires the document-management logic ({DocumentEngineBase}) with a * concrete access-control implementation. The authorization hooks are defined * here (role-based `AccessControl`), keeping the access control separate from - * the document-management logic (CMTAT / CMTA-RuleEngine pattern). It also wires + * the document-management logic (CMTAT / CMTA-RuleEngine pattern). The contract + * version is exposed through the {VersionModule} (ERC-8303), and it also wires * the ERC-2771 (gasless) meta-transaction support. */ -contract DocumentEngine is DocumentEngineBase, AccessControl, ERC2771Context { - /** - * @notice - * Get the current version of the smart contract - */ - string public constant VERSION = "0.4.0"; - +contract DocumentEngine is + DocumentEngineBase, + VersionModule, + AccessControl, + ERC2771Context +{ // Constructor to initialize the admin role constructor( address admin, @@ -72,6 +73,16 @@ contract DocumentEngine is DocumentEngineBase, AccessControl, ERC2771Context { return AccessControl.hasRole(role, account); } + /** + * @dev Combines the ERC-165 interface discovery of the version module + * (ERC-8303) with `AccessControl`. See {IERC165-supportsInterface}. + */ + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(VersionModule, AccessControl) returns (bool) { + return super.supportsInterface(interfaceId); + } + /*////////////////////////////////////////////////////////////// ERC2771 //////////////////////////////////////////////////////////////*/ diff --git a/src/interfaces/IERC8303.sol b/src/interfaces/IERC8303.sol new file mode 100644 index 0000000..a52a77c --- /dev/null +++ b/src/interfaces/IERC8303.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.20; + +/** + * @title IERC8303 - Contract Version + * @notice Interface for exposing a contract implementation version string. + * @dev ERC-8303 (Draft) — https://ethereum-magicians.org/t/erc-8303-contract-version/28795 + * The interface id is `0x54fd4d50` (the `version()` selector). + */ +interface IERC8303 { + /// @notice Returns the implementation version string. + /// @return The version value, for example "1.0.0". + function version() external view returns (string memory); +} diff --git a/src/modules/VersionModule.sol b/src/modules/VersionModule.sol new file mode 100644 index 0000000..86ce656 --- /dev/null +++ b/src/modules/VersionModule.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.20; + +import {ERC165} from "OZ/utils/introspection/ERC165.sol"; +import {IERC8303} from "../interfaces/IERC8303.sol"; + +/** + * @title VersionModule + * @notice Exposes the current contract version through ERC-8303 (`version()`), + * with optional ERC-165 interface discovery. + * @dev Implements ERC-8303 (Draft). The version string is defined here so the + * version concern is isolated in a dedicated module (CMTAT pattern). A deployment + * contract that also implements ERC-165 must combine this module's + * {supportsInterface} with the others it inherits. + */ +abstract contract VersionModule is IERC8303, ERC165 { + /** + * @notice Get the current version of the smart contract. + * @dev Follows Semantic Versioning 2.0.0 (`MAJOR.MINOR.PATCH`). + */ + string public constant VERSION = "0.4.0"; + + /** + * @inheritdoc IERC8303 + */ + function version() + public + view + virtual + override(IERC8303) + returns (string memory version_) + { + return VERSION; + } + + /** + * @dev Advertises ERC-8303 support (interface id `0x54fd4d50`). + * See {IERC165-supportsInterface}. + */ + function supportsInterface( + bytes4 interfaceId + ) public view virtual override returns (bool) { + return + interfaceId == type(IERC8303).interfaceId || + super.supportsInterface(interfaceId); + } +} diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index 7104913..c0c61e9 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -5,6 +5,8 @@ import "forge-std/Test.sol"; import "../src/DocumentEngine.sol"; import "../src/DocumentEngineInvariant.sol"; import "OZ/access/AccessControl.sol"; +import {IERC165} from "OZ/utils/introspection/IERC165.sol"; +import {IERC8303} from "../src/interfaces/IERC8303.sol"; import {DocumentEngineModule} from "CMTAT/modules/wrapper/options/DocumentEngineModule.sol"; /** @@ -306,6 +308,39 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { assertEq(doc.documentHash, documentHash); } + /*////////////////////////////////////////////////////////////// + Version (ERC-8303) + //////////////////////////////////////////////////////////////*/ + + function testVersionReturnsNonEmptyString() public { + string memory v = documentEngine.version(); + assertGt(bytes(v).length, 0); + assertEq(v, "0.4.0"); + // the public VERSION constant matches version() + assertEq(documentEngine.VERSION(), v); + } + + function testSupportsInterfaceERC8303() public { + // interface id declared by ERC-8303 + assertEq(type(IERC8303).interfaceId, bytes4(0x54fd4d50)); + assertTrue( + documentEngine.supportsInterface(type(IERC8303).interfaceId) + ); + } + + function testSupportsInterfaceERC165AndAccessControl() public { + assertTrue( + documentEngine.supportsInterface(type(IERC165).interfaceId) + ); + assertTrue( + documentEngine.supportsInterface(type(IAccessControl).interfaceId) + ); + } + + function testDoesNotSupportInvalidInterface() public { + assertFalse(documentEngine.supportsInterface(bytes4(0xffffffff))); + } + /*////////////////////////////////////////////////////////////// Set documents //////////////////////////////////////////////////////////////*/ From 183b648d742af518243c3820e0da65f1d935ef7f Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 08:01:35 +0200 Subject: [PATCH 05/47] feat: use AccessControlEnumerable and add DocumentEngineOwnable (Ownable2Step) deployment --- AGENTS.md | 26 +++--- CHANGELOG.md | 7 +- CLAUDE.md | 26 +++--- README.md | 13 ++- src/DocumentEngine.sol | 26 ++++-- src/DocumentEngineOwnable.sol | 131 ++++++++++++++++++++++++++++ test/DocumentEngineOwnable.t.sol | 144 +++++++++++++++++++++++++++++++ 7 files changed, 339 insertions(+), 34 deletions(-) create mode 100644 src/DocumentEngineOwnable.sol create mode 100644 test/DocumentEngineOwnable.t.sol diff --git a/AGENTS.md b/AGENTS.md index 62f2967..94112be 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -52,9 +52,11 @@ src/ ├── DocumentEngineBase.sol # Abstract base: ERC-1643 document logic + storage, │ # both management paths, batch functions, modifiers, │ # and the ABSTRACT _authorize* hooks (no access control) -├── DocumentEngine.sol # Deployment contract: defines the ACCESS CONTROL -│ # (AccessControl, _authorize* impls, hasRole), ERC-2771, -│ # combined supportsInterface, constructor +├── DocumentEngine.sol # Deployment #1: role-based access control +│ # (AccessControlEnumerable, _authorize* impls, hasRole), +│ # ERC-2771, combined supportsInterface, constructor +├── DocumentEngineOwnable.sol # Deployment #2: Ownable2Step (single owner) instead of +│ # roles; owner-managed token binding (setTokenBinding) ├── DocumentEngineInvariant.sol # Errors, roles (DOCUMENT_MANAGER_ROLE, │ # TOKEN_CONTRACT_ROLE) and the optional multi-token events ├── interfaces/ @@ -64,16 +66,20 @@ src/ # holds the VERSION constant (currently "0.4.0") test/ -└── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin path, - # bound-token path, batch ops, CMTAT integration - # (CMTATDocumentEngineMock built on DocumentEngineModule), - # flexible-authorization override (OpenDocumentEngine) +├── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin path, +│ # bound-token path, batch ops, ERC-8303, CMTAT integration +│ # (CMTATDocumentEngineMock built on DocumentEngineModule), +│ # flexible-authorization override (OpenDocumentEngine) +└── DocumentEngineOwnable.t.sol # Tests for the Ownable2Step deployment (owner path, + # token binding, two-step ownership, ERC-8303) ``` **Contract split (CMTAT module/deployment pattern):** `DocumentEngineBase` holds -the document logic and abstract `_authorize*` hooks; `DocumentEngine` is the -deployable contract that supplies the concrete access control. Add new -management logic in the base; change *who* is authorized in the deployment. +the document logic and abstract `_authorize*` hooks; each deployment contract +supplies the concrete access control. There are two deployments — +`DocumentEngine` (role-based, `AccessControlEnumerable`) and +`DocumentEngineOwnable` (`Ownable2Step`). Add new management logic in the base; +change *who* is authorized in a deployment (implement the `_authorize*` hooks). Other important files: diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a6b332..4f74716 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,7 +61,12 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) - **Optional multi-token events**: alongside the standard `IERC1643` events, the engine now also emits `DocumentUpdatedForContract` / `DocumentRemovedForContract`, which carry the `smartContract` (token) address so off-chain indexers can tell which contract a document belongs to during multi-contract operations. See [`ERC-1643-proposition.md`](./ERC-1643-proposition.md) for the proposed optional standard extension. - **Flexible access control (CMTAT / RuleEngine pattern)**: the restricted functions now use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to overridable `internal virtual` authorization hooks `_authorizeDocumentManagement()` / `_authorizeBoundTokenDocumentManagement()` (default: `DOCUMENT_MANAGER_ROLE` / `TOKEN_CONTRACT_ROLE`). This separates the document-management implementation from the authorization logic, so a subclass can change *who* is authorized without touching the management functions. Default behavior is unchanged. - **Split into a base contract and a deployment contract** (CMTAT module/deployment pattern): the document-management logic and storage now live in the new abstract `DocumentEngineBase` (with abstract `_authorize*` hooks), while `DocumentEngine` is the deployment contract that defines the access control (`AccessControl`, the concrete hooks and `hasRole`) and the ERC-2771 wiring. The deployable `DocumentEngine` API and behavior are unchanged. -- **Version module implementing ERC-8303**: the version is now exposed through a dedicated `VersionModule` (`src/modules/VersionModule.sol`) implementing the `IERC8303` interface (`src/interfaces/IERC8303.sol`). It adds a standard `version()` view function (in addition to the existing public `VERSION` constant) and advertises ERC-8303 via ERC-165 (`supportsInterface(0x54fd4d50) == true`). `DocumentEngine` combines the module's `supportsInterface` with `AccessControl`. +- **Version module implementing ERC-8303**: the version is now exposed through a dedicated `VersionModule` (`src/modules/VersionModule.sol`) implementing the `IERC8303` interface (`src/interfaces/IERC8303.sol`). It adds a standard `version()` view function (in addition to the existing public `VERSION` constant) and advertises ERC-8303 via ERC-165 (`supportsInterface(0x54fd4d50) == true`). `DocumentEngine` combines the module's `supportsInterface` with the access-control base. +- **Second deployment `DocumentEngineOwnable`** (`src/DocumentEngineOwnable.sol`): an alternative deployment that uses OpenZeppelin `Ownable2Step` (single owner, two-step transfer) instead of role-based access control, reusing the same `DocumentEngineBase` logic. Admin document management is restricted to the `owner`; the bound-token path uses an owner-managed binding allowlist (`setTokenBinding` / `isBoundToken`, the analog of `TOKEN_CONTRACT_ROLE`, reverting with `NotBoundToken`). + +### Changed (access control) + +- `DocumentEngine` now inherits **`AccessControlEnumerable`** instead of `AccessControl`, adding on-chain enumeration of role members (`getRoleMember`, `getRoleMemberCount`) and advertising `IAccessControlEnumerable` via ERC-165. Default authorization behavior is unchanged. ### Notes / bottlenecks diff --git a/CLAUDE.md b/CLAUDE.md index 62f2967..94112be 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,9 +52,11 @@ src/ ├── DocumentEngineBase.sol # Abstract base: ERC-1643 document logic + storage, │ # both management paths, batch functions, modifiers, │ # and the ABSTRACT _authorize* hooks (no access control) -├── DocumentEngine.sol # Deployment contract: defines the ACCESS CONTROL -│ # (AccessControl, _authorize* impls, hasRole), ERC-2771, -│ # combined supportsInterface, constructor +├── DocumentEngine.sol # Deployment #1: role-based access control +│ # (AccessControlEnumerable, _authorize* impls, hasRole), +│ # ERC-2771, combined supportsInterface, constructor +├── DocumentEngineOwnable.sol # Deployment #2: Ownable2Step (single owner) instead of +│ # roles; owner-managed token binding (setTokenBinding) ├── DocumentEngineInvariant.sol # Errors, roles (DOCUMENT_MANAGER_ROLE, │ # TOKEN_CONTRACT_ROLE) and the optional multi-token events ├── interfaces/ @@ -64,16 +66,20 @@ src/ # holds the VERSION constant (currently "0.4.0") test/ -└── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin path, - # bound-token path, batch ops, CMTAT integration - # (CMTATDocumentEngineMock built on DocumentEngineModule), - # flexible-authorization override (OpenDocumentEngine) +├── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin path, +│ # bound-token path, batch ops, ERC-8303, CMTAT integration +│ # (CMTATDocumentEngineMock built on DocumentEngineModule), +│ # flexible-authorization override (OpenDocumentEngine) +└── DocumentEngineOwnable.t.sol # Tests for the Ownable2Step deployment (owner path, + # token binding, two-step ownership, ERC-8303) ``` **Contract split (CMTAT module/deployment pattern):** `DocumentEngineBase` holds -the document logic and abstract `_authorize*` hooks; `DocumentEngine` is the -deployable contract that supplies the concrete access control. Add new -management logic in the base; change *who* is authorized in the deployment. +the document logic and abstract `_authorize*` hooks; each deployment contract +supplies the concrete access control. There are two deployments — +`DocumentEngine` (role-based, `AccessControlEnumerable`) and +`DocumentEngineOwnable` (`Ownable2Step`). Add new management logic in the base; +change *who* is authorized in a deployment (implement the `_authorize*` hooks). Other important files: diff --git a/README.md b/README.md index fc70b03..229a559 100644 --- a/README.md +++ b/README.md @@ -117,9 +117,14 @@ The engine is split into two contracts (CMTAT module/deployment pattern): `onlyBoundToken` modifiers and the **abstract** `_authorize*` hooks. It is agnostic to the access-control implementation. - **`DocumentEngine`** (deployment) — the concrete, deployable contract. It - defines the **access control** (`AccessControl`, the `_authorize*` hook - implementations and the `hasRole` override) and wires the ERC-2771 (gasless) - support. + defines the **access control** (`AccessControlEnumerable`, the `_authorize*` + hook implementations and the `hasRole` override) and wires the ERC-2771 + (gasless) support. `AccessControlEnumerable` additionally allows enumerating + the members of each role on-chain. +- **`DocumentEngineOwnable`** (alternative deployment) — same base logic, but + access control is a single **owner** via `Ownable2Step` (two-step ownership + transfer) instead of roles. Admin management is `owner`-only; the bound-token + path uses an owner-managed allowlist (`setTokenBinding` / `isBoundToken`). `DocumentEngineInvariant` provides the shared errors, roles (`DOCUMENT_MANAGER_ROLE`, `TOKEN_CONTRACT_ROLE`) and the optional multi-token events. @@ -171,7 +176,7 @@ interface IERC8303 { | :----------------: | :------------------: | :----------------------------------------------: | :------------: | :-----------: | | └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | | | | | | | -| **DocumentEngine** | Implementation | DocumentEngineBase, VersionModule, AccessControl, ERC2771Context | | | +| **DocumentEngine** | Implementation | DocumentEngineBase, VersionModule, AccessControlEnumerable, ERC2771Context | | | | └ | | Public ❗️ | 🛑 | NO❗️ | | └ | setDocument | Public ❗️ | 🛑 | onlyDocumentManager | | └ | removeDocument | External ❗️ | 🛑 | onlyDocumentManager | diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index e28932e..3c62b8a 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -1,7 +1,8 @@ //SPDX-License-Identifier: MPL-2.0 pragma solidity ^0.8.20; -import "OZ/access/AccessControl.sol"; +import "OZ/access/extensions/AccessControlEnumerable.sol"; +import {IAccessControl} from "OZ/access/IAccessControl.sol"; import "OZ/metatx/ERC2771Context.sol"; import "./DocumentEngineBase.sol"; import "./modules/VersionModule.sol"; @@ -11,15 +12,16 @@ import "./modules/VersionModule.sol"; * @notice Deployment contract to manage documents on-chain through ERC-1643. * @dev Wires the document-management logic ({DocumentEngineBase}) with a * concrete access-control implementation. The authorization hooks are defined - * here (role-based `AccessControl`), keeping the access control separate from - * the document-management logic (CMTAT / CMTA-RuleEngine pattern). The contract + * here (role-based `AccessControlEnumerable`, which additionally allows + * enumerating role members), keeping the access control separate from the + * document-management logic (CMTAT / CMTA-RuleEngine pattern). The contract * version is exposed through the {VersionModule} (ERC-8303), and it also wires * the ERC-2771 (gasless) meta-transaction support. */ contract DocumentEngine is DocumentEngineBase, VersionModule, - AccessControl, + AccessControlEnumerable, ERC2771Context { // Constructor to initialize the admin role @@ -65,21 +67,27 @@ contract DocumentEngine is function hasRole( bytes32 role, address account - ) public view virtual override returns (bool) { + ) public view virtual override(AccessControl, IAccessControl) returns (bool) { // The Default Admin has all roles - if (AccessControl.hasRole(DEFAULT_ADMIN_ROLE, account)) { + if (super.hasRole(DEFAULT_ADMIN_ROLE, account)) { return true; } - return AccessControl.hasRole(role, account); + return super.hasRole(role, account); } /** * @dev Combines the ERC-165 interface discovery of the version module - * (ERC-8303) with `AccessControl`. See {IERC165-supportsInterface}. + * (ERC-8303) with `AccessControlEnumerable`. See {IERC165-supportsInterface}. */ function supportsInterface( bytes4 interfaceId - ) public view virtual override(VersionModule, AccessControl) returns (bool) { + ) + public + view + virtual + override(VersionModule, AccessControlEnumerable) + returns (bool) + { return super.supportsInterface(interfaceId); } diff --git a/src/DocumentEngineOwnable.sol b/src/DocumentEngineOwnable.sol new file mode 100644 index 0000000..976ea3c --- /dev/null +++ b/src/DocumentEngineOwnable.sol @@ -0,0 +1,131 @@ +//SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.20; + +import {Ownable} from "OZ/access/Ownable.sol"; +import {Ownable2Step} from "OZ/access/Ownable2Step.sol"; +import "OZ/metatx/ERC2771Context.sol"; +import "./DocumentEngineBase.sol"; +import "./modules/VersionModule.sol"; + +/** + * @title DocumentEngineOwnable + * @notice Alternative deployment of the DocumentEngine that uses a single owner + * ({Ownable2Step}) instead of role-based access control. + * @dev Reuses the same document-management logic ({DocumentEngineBase}) and only + * swaps the access-control implementation, illustrating the base/deployment + * separation: + * - admin path (`onlyDocumentManager`): restricted to the `owner`; + * - bound-token path (`onlyBoundToken`): restricted to tokens the owner has + * bound to the engine (owner-managed allowlist, the analog of the role-based + * `TOKEN_CONTRACT_ROLE` binding). A bound token manages only its own documents. + * Ownership uses the two-step transfer flow for safety, and the contract also + * exposes its version through ERC-8303 ({VersionModule}) and wires ERC-2771. + */ +contract DocumentEngineOwnable is + DocumentEngineBase, + VersionModule, + Ownable2Step, + ERC2771Context +{ + /// @dev Tokens bound to the engine, allowed to manage their own documents. + mapping(address => bool) private _boundTokens; + + /// @notice Emitted when a token binding is set or removed by the owner. + event TokenBindingSet(address indexed token, bool bound); + + /// @notice Thrown when a non-bound caller uses the bound-token path. + error NotBoundToken(address caller); + + /** + * @param owner_ initial owner of the contract + * @param forwarderIrrevocable address of the ERC-2771 forwarder (gasless support) + */ + constructor( + address owner_, + address forwarderIrrevocable + ) Ownable(owner_) ERC2771Context(forwarderIrrevocable) {} + + /*////////////////////////////////////////////////////////////// + TOKEN BINDING + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Bind or unbind a token, allowing it to manage its own documents. + * @dev Owner-managed analog of granting/revoking `TOKEN_CONTRACT_ROLE`. + */ + function setTokenBinding(address token, bool bound) external onlyOwner { + _boundTokens[token] = bound; + emit TokenBindingSet(token, bound); + } + + /// @notice Returns whether `token` is bound to the engine. + function isBoundToken(address token) external view returns (bool) { + return _boundTokens[token]; + } + + /*////////////////////////////////////////////////////////////// + ACCESS CONTROL (implementation) + //////////////////////////////////////////////////////////////*/ + + /** + * @dev Authorization for the admin document-management path: only the owner. + */ + function _authorizeDocumentManagement() internal view virtual override { + _checkOwner(); + } + + /** + * @dev Authorization for the bound-token document-management path: the + * caller must be a token bound by the owner. + */ + function _authorizeBoundTokenDocumentManagement() + internal + view + virtual + override + { + if (!_boundTokens[_msgSender()]) { + revert NotBoundToken(_msgSender()); + } + } + + /*////////////////////////////////////////////////////////////// + ERC2771 + //////////////////////////////////////////////////////////////*/ + + /** + * @dev This surcharge is not necessary if you do not use ERC2771 + */ + function _msgSender() + internal + view + override(ERC2771Context, Context) + returns (address sender) + { + return ERC2771Context._msgSender(); + } + + /** + * @dev This surcharge is not necessary if you do not use ERC2771 + */ + function _msgData() + internal + view + override(ERC2771Context, Context) + returns (bytes calldata) + { + return ERC2771Context._msgData(); + } + + /** + * @dev This surcharge is not necessary if you do not use the MetaTxModule + */ + function _contextSuffixLength() + internal + view + override(ERC2771Context, Context) + returns (uint256) + { + return ERC2771Context._contextSuffixLength(); + } +} diff --git a/test/DocumentEngineOwnable.t.sol b/test/DocumentEngineOwnable.t.sol new file mode 100644 index 0000000..3117c94 --- /dev/null +++ b/test/DocumentEngineOwnable.t.sol @@ -0,0 +1,144 @@ +//SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.20; + +import "forge-std/Test.sol"; +import "../src/DocumentEngineOwnable.sol"; +import {Ownable} from "OZ/access/Ownable.sol"; +import {IAccessControl} from "OZ/access/IAccessControl.sol"; +import {IERC165} from "OZ/utils/introspection/IERC165.sol"; +import {IERC8303} from "../src/interfaces/IERC8303.sol"; + +contract DocumentEngineOwnableTest is Test { + DocumentEngineOwnable public engine; + address public owner = address(0x1); + address public newOwner = address(0x2); + address public attacker = address(0x3); + address private testContract = address(0x4); + bytes32 public documentName = keccak256("doc1"); + string public documentURI = "https://example.com/doc1"; + bytes32 public documentHash = keccak256("doc1Hash"); + address AddressZero = address(0); + + function setUp() public { + engine = new DocumentEngineOwnable(owner, AddressZero); + } + + /* ============ DEPLOYMENT ============ */ + + function testDeploySetsOwner() public { + assertEq(engine.owner(), owner); + } + + function testDeployRevertsWithZeroOwner() public { + vm.expectRevert( + abi.encodeWithSelector(Ownable.OwnableInvalidOwner.selector, AddressZero) + ); + new DocumentEngineOwnable(AddressZero, AddressZero); + } + + /* ============ ADMIN PATH (owner) ============ */ + + function testOwnerCanSetAndRemoveDocument() public { + vm.prank(owner); + engine.setDocument(testContract, documentName, documentURI, documentHash); + + IERC1643.Document memory doc = engine.getDocument( + testContract, + documentName + ); + assertEq(doc.uri, documentURI); + assertEq(doc.documentHash, documentHash); + assertEq(doc.lastModified, block.timestamp); + + vm.prank(owner); + engine.removeDocument(testContract, documentName); + doc = engine.getDocument(testContract, documentName); + assertEq(doc.lastModified, 0); + } + + function testNonOwnerCannotSetDocument() public { + vm.prank(attacker); + vm.expectRevert( + abi.encodeWithSelector( + Ownable.OwnableUnauthorizedAccount.selector, + attacker + ) + ); + engine.setDocument(testContract, documentName, documentURI, documentHash); + } + + /* ============ BOUND-TOKEN PATH ============ */ + + function testOwnerCanBindToken() public { + vm.prank(owner); + engine.setTokenBinding(testContract, true); + assertTrue(engine.isBoundToken(testContract)); + } + + function testNonOwnerCannotBindToken() public { + vm.prank(attacker); + vm.expectRevert( + abi.encodeWithSelector( + Ownable.OwnableUnauthorizedAccount.selector, + attacker + ) + ); + engine.setTokenBinding(testContract, true); + } + + function testBoundTokenCanManageOwnDocument() public { + vm.prank(owner); + engine.setTokenBinding(testContract, true); + + vm.prank(testContract); + engine.setDocument(documentName, documentURI, documentHash); + + IERC1643.Document memory doc = engine.getDocument( + testContract, + documentName + ); + assertEq(doc.uri, documentURI); + + vm.prank(testContract); + engine.removeDocument(documentName); + doc = engine.getDocument(testContract, documentName); + assertEq(doc.lastModified, 0); + } + + function testUnboundTokenCannotSelfManage() public { + vm.prank(attacker); + vm.expectRevert( + abi.encodeWithSelector( + DocumentEngineOwnable.NotBoundToken.selector, + attacker + ) + ); + engine.setDocument(documentName, documentURI, documentHash); + } + + /* ============ TWO-STEP OWNERSHIP ============ */ + + function testTwoStepOwnershipTransfer() public { + vm.prank(owner); + engine.transferOwnership(newOwner); + // ownership not transferred until accepted + assertEq(engine.owner(), owner); + assertEq(engine.pendingOwner(), newOwner); + + vm.prank(newOwner); + engine.acceptOwnership(); + assertEq(engine.owner(), newOwner); + assertEq(engine.pendingOwner(), AddressZero); + } + + /* ============ VERSION (ERC-8303) ============ */ + + function testVersionAndInterface() public { + assertEq(engine.version(), "0.4.0"); + assertTrue(engine.supportsInterface(type(IERC8303).interfaceId)); + assertTrue(engine.supportsInterface(type(IERC165).interfaceId)); + // no role-based access control here + assertFalse(engine.supportsInterface(type(IAccessControl).interfaceId)); + assertFalse(engine.supportsInterface(bytes4(0xffffffff))); + } +} From 1b9e49e0dfd86879817e42cacd2be7e3637fa157 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 08:08:43 +0200 Subject: [PATCH 06/47] refactor: move role constants from DocumentEngineInvariant to DocumentEngine (role-free base/Ownable) --- AGENTS.md | 10 ++++++---- CHANGELOG.md | 1 + CLAUDE.md | 10 ++++++---- README.md | 6 ++++-- src/DocumentEngine.sol | 10 ++++++++++ src/DocumentEngineInvariant.sol | 18 ++++++++---------- test/DocumentEngine.t.sol | 6 ++++++ 7 files changed, 41 insertions(+), 20 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 94112be..76d8537 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,12 +53,14 @@ src/ │ # both management paths, batch functions, modifiers, │ # and the ABSTRACT _authorize* hooks (no access control) ├── DocumentEngine.sol # Deployment #1: role-based access control -│ # (AccessControlEnumerable, _authorize* impls, hasRole), -│ # ERC-2771, combined supportsInterface, constructor +│ # (AccessControlEnumerable, the role constants +│ # DOCUMENT_MANAGER_ROLE / TOKEN_CONTRACT_ROLE, _authorize* +│ # impls, hasRole), ERC-2771, supportsInterface, constructor ├── DocumentEngineOwnable.sol # Deployment #2: Ownable2Step (single owner) instead of │ # roles; owner-managed token binding (setTokenBinding) -├── DocumentEngineInvariant.sol # Errors, roles (DOCUMENT_MANAGER_ROLE, -│ # TOKEN_CONTRACT_ROLE) and the optional multi-token events +├── DocumentEngineInvariant.sol # Shared errors + optional multi-token events only +│ # (NO access-control specifics — roles live in the +│ # role-based deployment) ├── interfaces/ │ └── IERC8303.sol # ERC-8303 "Contract Version" interface (id 0x54fd4d50) └── modules/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f74716..d4b9cf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) ### Changed (access control) - `DocumentEngine` now inherits **`AccessControlEnumerable`** instead of `AccessControl`, adding on-chain enumeration of role members (`getRoleMember`, `getRoleMemberCount`) and advertising `IAccessControlEnumerable` via ERC-165. Default authorization behavior is unchanged. +- Moved the role constants `DOCUMENT_MANAGER_ROLE` / `TOKEN_CONTRACT_ROLE` out of the shared `DocumentEngineInvariant` and into the role-based `DocumentEngine`, so `DocumentEngineInvariant` (and the `DocumentEngineOwnable` deployment) no longer carry access-control-specific constants. The invariant now holds only the shared errors and multi-token events. ### Notes / bottlenecks diff --git a/CLAUDE.md b/CLAUDE.md index 94112be..76d8537 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -53,12 +53,14 @@ src/ │ # both management paths, batch functions, modifiers, │ # and the ABSTRACT _authorize* hooks (no access control) ├── DocumentEngine.sol # Deployment #1: role-based access control -│ # (AccessControlEnumerable, _authorize* impls, hasRole), -│ # ERC-2771, combined supportsInterface, constructor +│ # (AccessControlEnumerable, the role constants +│ # DOCUMENT_MANAGER_ROLE / TOKEN_CONTRACT_ROLE, _authorize* +│ # impls, hasRole), ERC-2771, supportsInterface, constructor ├── DocumentEngineOwnable.sol # Deployment #2: Ownable2Step (single owner) instead of │ # roles; owner-managed token binding (setTokenBinding) -├── DocumentEngineInvariant.sol # Errors, roles (DOCUMENT_MANAGER_ROLE, -│ # TOKEN_CONTRACT_ROLE) and the optional multi-token events +├── DocumentEngineInvariant.sol # Shared errors + optional multi-token events only +│ # (NO access-control specifics — roles live in the +│ # role-based deployment) ├── interfaces/ │ └── IERC8303.sol # ERC-8303 "Contract Version" interface (id 0x54fd4d50) └── modules/ diff --git a/README.md b/README.md index 229a559..4cfbfc1 100644 --- a/README.md +++ b/README.md @@ -126,8 +126,10 @@ The engine is split into two contracts (CMTAT module/deployment pattern): transfer) instead of roles. Admin management is `owner`-only; the bound-token path uses an owner-managed allowlist (`setTokenBinding` / `isBoundToken`). -`DocumentEngineInvariant` provides the shared errors, roles -(`DOCUMENT_MANAGER_ROLE`, `TOKEN_CONTRACT_ROLE`) and the optional multi-token events. +`DocumentEngineInvariant` provides the errors and the optional multi-token events +shared by every deployment. Access-control specifics are **not** defined there: +the role constants (`DOCUMENT_MANAGER_ROLE`, `TOKEN_CONTRACT_ROLE`) live in the +role-based `DocumentEngine`, and the owner/binding logic in `DocumentEngineOwnable`. `VersionModule` (`src/modules/VersionModule.sol`) isolates the version concern and implements [ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index 3c62b8a..d1d16ff 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -24,6 +24,16 @@ contract DocumentEngine is AccessControlEnumerable, ERC2771Context { + // Role allowed to manage documents on behalf of any smart contract (admin path) + bytes32 public constant DOCUMENT_MANAGER_ROLE = + keccak256("DOCUMENT_MANAGER_ROLE"); + + // Role granted to a token bound to the engine, allowing it to manage its own + // documents through the standard ERC-1643 functions (msg.sender is the token). + // Mirrors the RuleEngine binding pattern (CMTA/RuleEngine `TOKEN_CONTRACT_ROLE`). + bytes32 public constant TOKEN_CONTRACT_ROLE = + keccak256("TOKEN_CONTRACT_ROLE"); + // Constructor to initialize the admin role constructor( address admin, diff --git a/src/DocumentEngineInvariant.sol b/src/DocumentEngineInvariant.sol index 2d229fd..a554bfd 100644 --- a/src/DocumentEngineInvariant.sol +++ b/src/DocumentEngineInvariant.sol @@ -1,6 +1,14 @@ //SPDX-License-Identifier: MPL-2.0 pragma solidity ^0.8.20; +/** + * @title DocumentEngineInvariant + * @notice Shared errors and events for the DocumentEngine, common to every + * deployment regardless of its access-control model. + * @dev Access-control specifics (roles, owner, ...) are intentionally NOT + * defined here; they belong to the deployment contract (e.g. the role constants + * live in {DocumentEngine}, the owner logic in {DocumentEngineOwnable}). + */ contract DocumentEngineInvariant { error DocumentNotFound(address smartContract, bytes32 name); error InvalidInputLength(); @@ -27,14 +35,4 @@ contract DocumentEngineInvariant { string uri, bytes32 documentHash ); - - // Role allowed to manage documents on behalf of any smart contract (admin path) - bytes32 public constant DOCUMENT_MANAGER_ROLE = - keccak256("DOCUMENT_MANAGER_ROLE"); - - // Role granted to a token bound to the engine, allowing it to manage its own - // documents through the standard ERC-1643 functions (msg.sender is the token). - // Mirrors the RuleEngine binding pattern (CMTA/RuleEngine `TOKEN_CONTRACT_ROLE`). - bytes32 public constant TOKEN_CONTRACT_ROLE = - keccak256("TOKEN_CONTRACT_ROLE"); } diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index c0c61e9..fdebd21 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -50,6 +50,12 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { string public documentURI = "https://example.com/doc1"; bytes32 public documentHash = keccak256("doc1Hash"); bytes32 public constant DOCUMENT_ROLE = keccak256("DOCUMENT_ROLE"); + // Roles are defined on the role-based deployment (DocumentEngine), not on the + // shared DocumentEngineInvariant; mirrored here for the assertions. + bytes32 public constant DOCUMENT_MANAGER_ROLE = + keccak256("DOCUMENT_MANAGER_ROLE"); + bytes32 public constant TOKEN_CONTRACT_ROLE = + keccak256("TOKEN_CONTRACT_ROLE"); address AddressZero = address(0); function setUp() public { From a6a8a7c225d357fad79246b8bc604a9f7c6df30a Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 08:31:34 +0200 Subject: [PATCH 07/47] revert: keep purpose-built token binding; document why RuleEngine's ERC-3643 compliance module is not reused --- AGENTS.md | 6 +++++- CHANGELOG.md | 6 +++--- CLAUDE.md | 6 +++++- README.md | 38 +++++++++++++++++++++++++++++++++++++- lib/RuleEngine | 2 +- 5 files changed, 51 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 76d8537..7774fbd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,6 +27,10 @@ addressed by a `bytes32` name. pattern). The standard single-arg `IERC1643` functions (`setDocument(name,uri,hash)`, `removeDocument(name)`) let a bound token manage its **own** namespace (`_msgSender()`). Bind a token with `grantRole(TOKEN_CONTRACT_ROLE, token)`. + NOTE: RuleEngine's `ERC3643ComplianceExtendedModule` is intentionally **not** + reused for binding — it is an `IERC3643Compliance`, which would drag in + transfer-compliance callbacks (`canTransfer`/`transferred`/`created`/`destroyed`) + irrelevant to a document engine. See the README rationale section. - **Events:** every write emits the standard `IERC1643` events **and** the optional `DocumentUpdatedForContract` / `DocumentRemovedForContract` events (which add the `smartContract` address). See `ERC-1643-proposition.md`. @@ -95,7 +99,7 @@ Other important files: ## Dependencies (tested versions) -- CMTAT `v3.3.0-rc1`, RuleEngine `v2.1.0` +- CMTAT `v3.3.0-rc1`, RuleEngine `v3.0.0-rc4` (binding-pattern reference only; compliance module not reused) - OpenZeppelin Contracts / Contracts Upgradeable `v5.6.1` - Solidity `0.8.34`, Foundry diff --git a/CHANGELOG.md b/CHANGELOG.md index d4b9cf4..b6a4b46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,7 +48,7 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) - **Dependencies** - Upgrade CMTAT `v2.5.0-rc0` → `v3.3.0-rc1` - Upgrade OpenZeppelin Contracts (and Contracts Upgradeable) `v5.0.2` → `v5.6.1` - - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) `v2.1.0` as a submodule (binding-role reference) + - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) `v3.0.0-rc4` as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) - **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) - **`IERC1643` (CMTAT v3) breaking changes** - `getDocument(bytes32)` now returns a `Document` struct instead of the `(string, bytes32, uint256)` tuple. Both `getDocument` overloads updated accordingly. @@ -57,7 +57,7 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) ### Added -- **Bound-token document management (RuleEngine binding pattern)**: implement the now-mandatory `IERC1643.setDocument(name, uri, hash)` and `removeDocument(name)`. They are gated by a new `TOKEN_CONTRACT_ROLE` and scoped to the caller (`_msgSender()`) own namespace. A token bound with `grantRole(TOKEN_CONTRACT_ROLE, token)` manages its own documents and can never affect another contract's documents. The existing admin overloads (explicit `address`, `DOCUMENT_MANAGER_ROLE`) are unchanged, so both systems work side by side. +- **Bound-token document management (RuleEngine binding pattern)**: implement the now-mandatory `IERC1643.setDocument(name, uri, hash)` and `removeDocument(name)`. They are gated by a new `TOKEN_CONTRACT_ROLE` and scoped to the caller (`_msgSender()`) own namespace. A token bound with `grantRole(TOKEN_CONTRACT_ROLE, token)` manages its own documents and can never affect another contract's documents. The existing admin overloads (explicit `address`, `DOCUMENT_MANAGER_ROLE`) are unchanged, so both systems work side by side. (RuleEngine's `ERC3643ComplianceExtendedModule` was evaluated for this but intentionally not reused — see the README.) - **Optional multi-token events**: alongside the standard `IERC1643` events, the engine now also emits `DocumentUpdatedForContract` / `DocumentRemovedForContract`, which carry the `smartContract` (token) address so off-chain indexers can tell which contract a document belongs to during multi-contract operations. See [`ERC-1643-proposition.md`](./ERC-1643-proposition.md) for the proposed optional standard extension. - **Flexible access control (CMTAT / RuleEngine pattern)**: the restricted functions now use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to overridable `internal virtual` authorization hooks `_authorizeDocumentManagement()` / `_authorizeBoundTokenDocumentManagement()` (default: `DOCUMENT_MANAGER_ROLE` / `TOKEN_CONTRACT_ROLE`). This separates the document-management implementation from the authorization logic, so a subclass can change *who* is authorized without touching the management functions. Default behavior is unchanged. - **Split into a base contract and a deployment contract** (CMTAT module/deployment pattern): the document-management logic and storage now live in the new abstract `DocumentEngineBase` (with abstract `_authorize*` hooks), while `DocumentEngine` is the deployment contract that defines the access control (`AccessControl`, the concrete hooks and `hasRole`) and the ERC-2771 wiring. The deployable `DocumentEngine` API and behavior are unchanged. @@ -67,7 +67,7 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) ### Changed (access control) - `DocumentEngine` now inherits **`AccessControlEnumerable`** instead of `AccessControl`, adding on-chain enumeration of role members (`getRoleMember`, `getRoleMemberCount`) and advertising `IAccessControlEnumerable` via ERC-165. Default authorization behavior is unchanged. -- Moved the role constants `DOCUMENT_MANAGER_ROLE` / `TOKEN_CONTRACT_ROLE` out of the shared `DocumentEngineInvariant` and into the role-based `DocumentEngine`, so `DocumentEngineInvariant` (and the `DocumentEngineOwnable` deployment) no longer carry access-control-specific constants. The invariant now holds only the shared errors and multi-token events. +- Moved the role constants (`DOCUMENT_MANAGER_ROLE`, `TOKEN_CONTRACT_ROLE`) out of the shared `DocumentEngineInvariant` and into the role-based `DocumentEngine`, so `DocumentEngineInvariant` (and the `DocumentEngineOwnable` deployment) no longer carry access-control-specific constants. The invariant now holds only the shared errors and multi-token events. ### Notes / bottlenecks diff --git a/CLAUDE.md b/CLAUDE.md index 76d8537..7774fbd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,6 +27,10 @@ addressed by a `bytes32` name. pattern). The standard single-arg `IERC1643` functions (`setDocument(name,uri,hash)`, `removeDocument(name)`) let a bound token manage its **own** namespace (`_msgSender()`). Bind a token with `grantRole(TOKEN_CONTRACT_ROLE, token)`. + NOTE: RuleEngine's `ERC3643ComplianceExtendedModule` is intentionally **not** + reused for binding — it is an `IERC3643Compliance`, which would drag in + transfer-compliance callbacks (`canTransfer`/`transferred`/`created`/`destroyed`) + irrelevant to a document engine. See the README rationale section. - **Events:** every write emits the standard `IERC1643` events **and** the optional `DocumentUpdatedForContract` / `DocumentRemovedForContract` events (which add the `smartContract` address). See `ERC-1643-proposition.md`. @@ -95,7 +99,7 @@ Other important files: ## Dependencies (tested versions) -- CMTAT `v3.3.0-rc1`, RuleEngine `v2.1.0` +- CMTAT `v3.3.0-rc1`, RuleEngine `v3.0.0-rc4` (binding-pattern reference only; compliance module not reused) - OpenZeppelin Contracts / Contracts Upgradeable `v5.6.1` - Solidity `0.8.34`, Foundry diff --git a/README.md b/README.md index 4cfbfc1..be739cc 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,10 @@ function setDocument(bytes32 name_, string calldata uri_, bytes32 documentHash_) function removeDocument(bytes32 name_) external; ``` +> This mirrors the RuleEngine *binding* pattern without reusing its +> `ERC3643ComplianceExtendedModule` — see +> [Why not reuse RuleEngine's ERC-3643 compliance module?](#why-not-reuse-ruleengines-erc-3643-compliance-module) below. + ### Flexible access control Following the CMTAT / [RuleEngine](https://github.com/CMTA/RuleEngine) pattern, @@ -89,6 +93,38 @@ logic: a subclass can override a hook to change *who* is authorized (e.g. a different role, an allowlist, or open access) without touching the management functions. The default behavior is the role checks shown above. +### Why not reuse RuleEngine's ERC-3643 compliance module? + +CMTA's [RuleEngine](https://github.com/CMTA/RuleEngine) (v3) ships an +`ERC3643ComplianceExtendedModule` that offers a ready-made token-binding registry +(`bindToken` / `unbindToken` / `isTokenBound` / `getTokenBounds`). It is tempting +to reuse it for the bound-token path, but we deliberately do **not**, because that +module is an **`IERC3643Compliance`** — a *transfer-compliance* contract. + +Inheriting it would force the DocumentEngine to also implement the ERC-3643 +transfer-compliance callbacks that come with that interface: + +```solidity +function canTransfer(address, address, uint256) external view returns (bool); +function transferred(address, address, uint256) external; +function created(address, uint256) external; +function destroyed(address, uint256) external; +``` + +A document engine has **nothing to do with token transfers**, so these would have +to be stubbed as no-ops (`canTransfer` always returning `true`). That is +misleading: the contract would advertise a transfer-compliance surface it does +not honor, enlarging the ABI and inviting integrators to wire it where a real +compliance contract is expected. + +The binding concept we actually need is tiny — "is this caller a token allowed to +manage its own documents?" — so we implement just that: a `TOKEN_CONTRACT_ROLE` +in the role-based `DocumentEngine` (exactly the RuleEngine *binding* mechanism, +which is role-based, not the compliance module) and an owner-managed allowlist in +`DocumentEngineOwnable`. This keeps the engine's surface honest and minimal while +still mirroring the RuleEngine binding pattern. The RuleEngine submodule is kept +as a reference for that pattern. + ### Events On every write, the engine emits the standard `IERC1643` events **and** the @@ -227,7 +263,7 @@ The toolchain includes the following components, where the versions are the late - OpenZeppelin Contracts (submodule) [v5.6.1](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.6.1) - Tests - [CMTAT v3.3.0-rc1](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc1) - - [RuleEngine v2.1.0](https://github.com/CMTA/RuleEngine/releases/tag/v2.1.0) (binding-role reference) + - [RuleEngine v3.0.0-rc4](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc4) (binding-pattern reference only — its compliance module is [not reused](#why-not-reuse-ruleengines-erc-3643-compliance-module)) - OpenZeppelin Contracts Upgradeable (submodule) [v5.6.1](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v5.6.1) ## Tools diff --git a/lib/RuleEngine b/lib/RuleEngine index 461d32f..66fcf2a 160000 --- a/lib/RuleEngine +++ b/lib/RuleEngine @@ -1 +1 @@ -Subproject commit 461d32fca6cf501d6c15f3aed5f18855b2a6581c +Subproject commit 66fcf2aafebd1f9d9de8a81dec92b88da071c9b3 From 8fcd114230cfb63433c4024f55d5ccad913f9c4b Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 12:54:10 +0200 Subject: [PATCH 08/47] Add multi token support --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6a4b46..f725a01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,7 +58,7 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) ### Added - **Bound-token document management (RuleEngine binding pattern)**: implement the now-mandatory `IERC1643.setDocument(name, uri, hash)` and `removeDocument(name)`. They are gated by a new `TOKEN_CONTRACT_ROLE` and scoped to the caller (`_msgSender()`) own namespace. A token bound with `grantRole(TOKEN_CONTRACT_ROLE, token)` manages its own documents and can never affect another contract's documents. The existing admin overloads (explicit `address`, `DOCUMENT_MANAGER_ROLE`) are unchanged, so both systems work side by side. (RuleEngine's `ERC3643ComplianceExtendedModule` was evaluated for this but intentionally not reused — see the README.) -- **Optional multi-token events**: alongside the standard `IERC1643` events, the engine now also emits `DocumentUpdatedForContract` / `DocumentRemovedForContract`, which carry the `smartContract` (token) address so off-chain indexers can tell which contract a document belongs to during multi-contract operations. See [`ERC-1643-proposition.md`](./ERC-1643-proposition.md) for the proposed optional standard extension. +- **Optional multi-token events**: alongside the standard `IERC1643` events, the engine now also emits `DocumentUpdatedForContract` / `DocumentRemovedForContract`, which carry the `smartContract` (token) address so off-chain indexers can tell which contract a document belongs to during multi-contract operations. See [`ERC-1643-proposition.md`](./doc/ERCSpecification/ERC-1643-proposition.md) for the proposed optional standard extension. - **Flexible access control (CMTAT / RuleEngine pattern)**: the restricted functions now use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to overridable `internal virtual` authorization hooks `_authorizeDocumentManagement()` / `_authorizeBoundTokenDocumentManagement()` (default: `DOCUMENT_MANAGER_ROLE` / `TOKEN_CONTRACT_ROLE`). This separates the document-management implementation from the authorization logic, so a subclass can change *who* is authorized without touching the management functions. Default behavior is unchanged. - **Split into a base contract and a deployment contract** (CMTAT module/deployment pattern): the document-management logic and storage now live in the new abstract `DocumentEngineBase` (with abstract `_authorize*` hooks), while `DocumentEngine` is the deployment contract that defines the access control (`AccessControl`, the concrete hooks and `hasRole`) and the ERC-2771 wiring. The deployable `DocumentEngine` API and behavior are unchanged. - **Version module implementing ERC-8303**: the version is now exposed through a dedicated `VersionModule` (`src/modules/VersionModule.sol`) implementing the `IERC8303` interface (`src/interfaces/IERC8303.sol`). It adds a standard `version()` view function (in addition to the existing public `VERSION` constant) and advertises ERC-8303 via ERC-165 (`supportsInterface(0x54fd4d50) == true`). `DocumentEngine` combines the module's `supportsInterface` with the access-control base. From 113a348d867605178a2143c27dc63eb793fba531 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 13:28:06 +0200 Subject: [PATCH 09/47] fix: conform DocumentEngine to updated ERC-1643 (emission responsibility, IERC1643MultiDocument, name/missing-doc guards, ERC-165) --- .gitignore | 2 + AGENTS.md | 21 ++- CHANGELOG.md | 9 ++ CLAUDE.md | 21 ++- README.md | 17 ++- doc/ERCSpecification/ERC-1643-proposition.md | 129 ------------------- doc/ERCSpecification/erc-1643.md | 64 ++++++++- src/DocumentEngine.sol | 15 ++- src/DocumentEngineBase.sol | 74 ++++++----- src/DocumentEngineInvariant.sol | 28 +--- src/DocumentEngineOwnable.sol | 15 +++ src/interfaces/IERC1643MultiDocument.sol | 57 ++++++++ test/DocumentEngine.t.sol | 47 +++++++ 13 files changed, 292 insertions(+), 207 deletions(-) delete mode 100644 doc/ERCSpecification/ERC-1643-proposition.md create mode 100644 src/interfaces/IERC1643MultiDocument.sol diff --git a/.gitignore b/.gitignore index d9e0058..810321e 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ docs/ # Dotenv file .env + +history diff --git a/AGENTS.md b/AGENTS.md index 7774fbd..6d4f5ca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,9 +31,15 @@ addressed by a `bytes32` name. reused for binding — it is an `IERC3643Compliance`, which would drag in transfer-compliance callbacks (`canTransfer`/`transferred`/`created`/`destroyed`) irrelevant to a document engine. See the README rationale section. -- **Events:** every write emits the standard `IERC1643` events **and** the optional - `DocumentUpdatedForContract` / `DocumentRemovedForContract` events (which add the - `smartContract` address). See `ERC-1643-proposition.md`. +- **Events (ERC-1643 emission responsibility):** this engine is a *shared, + multi-token* manager, so it emits **only** the address-carrying extension events + `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` (param `subject`) and + **not** the base `DocumentUpdated` / `DocumentRemoved` (those carry no address and + are the token contract's responsibility). Extension declared in + `src/interfaces/IERC1643MultiDocument.sol`; rationale in `ERC-1643-proposition.md`. +- **ERC-1643 conformance:** `setDocument` reverts `ERC1643InvalidName()` on + `name == 0`; `removeDocument` reverts `ERC1643MissingDocument()` on a missing doc; + `supportsInterface` advertises `IERC1643` + `IERC1643MultiDocument` (both deployments). - **ERC-2771:** meta-transaction (gasless) support; `_msgSender()` is used everywhere. - **Access control:** `DEFAULT_ADMIN_ROLE` implicitly has every role (see the `hasRole` override). @@ -62,11 +68,12 @@ src/ │ # impls, hasRole), ERC-2771, supportsInterface, constructor ├── DocumentEngineOwnable.sol # Deployment #2: Ownable2Step (single owner) instead of │ # roles; owner-managed token binding (setTokenBinding) -├── DocumentEngineInvariant.sol # Shared errors + optional multi-token events only -│ # (NO access-control specifics — roles live in the -│ # role-based deployment) +├── DocumentEngineInvariant.sol # Shared errors only (incl. ERC1643InvalidName / +│ # ERC1643MissingDocument); NO access-control specifics ├── interfaces/ -│ └── IERC8303.sol # ERC-8303 "Contract Version" interface (id 0x54fd4d50) +│ ├── IERC8303.sol # ERC-8303 "Contract Version" interface (id 0x54fd4d50) +│ └── IERC1643MultiDocument.sol # Multi-token ERC-1643 extension (address-scoped fns + +│ # DocumentUpdatedForSubject / DocumentRemovedForSubject) └── modules/ └── VersionModule.sol # Version module: implements ERC-8303 version() + ERC-165, # holds the VERSION constant (currently "0.4.0") diff --git a/CHANGELOG.md b/CHANGELOG.md index f725a01..452f7f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,15 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) - `DocumentEngine` now inherits **`AccessControlEnumerable`** instead of `AccessControl`, adding on-chain enumeration of role members (`getRoleMember`, `getRoleMemberCount`) and advertising `IAccessControlEnumerable` via ERC-165. Default authorization behavior is unchanged. - Moved the role constants (`DOCUMENT_MANAGER_ROLE`, `TOKEN_CONTRACT_ROLE`) out of the shared `DocumentEngineInvariant` and into the role-based `DocumentEngine`, so `DocumentEngineInvariant` (and the `DocumentEngineOwnable` deployment) no longer carry access-control-specific constants. The invariant now holds only the shared errors and multi-token events. +### Fixed (ERC-1643 conformance) + +Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/erc-1643.md) (which now folds in the multi-token extension and the emission-responsibility rules): + +- **Emission responsibility.** As a shared, multi-token manager the engine now emits **only** the address-carrying extension events and **no longer** emits the base `DocumentUpdated` / `DocumentRemoved` events (the spec's `MUST NOT` for a shared manager — those events carry no `subject` and belong on the token contract). +- **Extension events/interface.** Renamed the multi-token events to the standard `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` (parameter `subject`), and introduced the `IERC1643MultiDocument` interface (`src/interfaces/IERC1643MultiDocument.sol`) that the base now implements — the address-scoped `getDocument` / `getAllDocuments` / `setDocument` / `removeDocument`. +- **Input validation.** `setDocument` now reverts `ERC1643InvalidName()` when `name == bytes32(0)`; `removeDocument` now reverts `ERC1643MissingDocument()` for a non-existent document (previously it silently emitted a spurious removal event). +- **ERC-165 discovery.** `supportsInterface` now returns `true` for `type(IERC1643).interfaceId` and `type(IERC1643MultiDocument).interfaceId` (both deployments). + ### Notes / bottlenecks - CMTAT v3 no longer ships a *standalone* token that consumes an external document engine through its constructor; the standard token stores documents on-chain (`DocumentERC1643Module`). External-engine integration now goes through CMTAT's `DocumentEngineModule` (`setDocumentEngine`). The test suite was updated to exercise this real integration path via a minimal token built on `DocumentEngineModule`. diff --git a/CLAUDE.md b/CLAUDE.md index 7774fbd..6d4f5ca 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -31,9 +31,15 @@ addressed by a `bytes32` name. reused for binding — it is an `IERC3643Compliance`, which would drag in transfer-compliance callbacks (`canTransfer`/`transferred`/`created`/`destroyed`) irrelevant to a document engine. See the README rationale section. -- **Events:** every write emits the standard `IERC1643` events **and** the optional - `DocumentUpdatedForContract` / `DocumentRemovedForContract` events (which add the - `smartContract` address). See `ERC-1643-proposition.md`. +- **Events (ERC-1643 emission responsibility):** this engine is a *shared, + multi-token* manager, so it emits **only** the address-carrying extension events + `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` (param `subject`) and + **not** the base `DocumentUpdated` / `DocumentRemoved` (those carry no address and + are the token contract's responsibility). Extension declared in + `src/interfaces/IERC1643MultiDocument.sol`; rationale in `ERC-1643-proposition.md`. +- **ERC-1643 conformance:** `setDocument` reverts `ERC1643InvalidName()` on + `name == 0`; `removeDocument` reverts `ERC1643MissingDocument()` on a missing doc; + `supportsInterface` advertises `IERC1643` + `IERC1643MultiDocument` (both deployments). - **ERC-2771:** meta-transaction (gasless) support; `_msgSender()` is used everywhere. - **Access control:** `DEFAULT_ADMIN_ROLE` implicitly has every role (see the `hasRole` override). @@ -62,11 +68,12 @@ src/ │ # impls, hasRole), ERC-2771, supportsInterface, constructor ├── DocumentEngineOwnable.sol # Deployment #2: Ownable2Step (single owner) instead of │ # roles; owner-managed token binding (setTokenBinding) -├── DocumentEngineInvariant.sol # Shared errors + optional multi-token events only -│ # (NO access-control specifics — roles live in the -│ # role-based deployment) +├── DocumentEngineInvariant.sol # Shared errors only (incl. ERC1643InvalidName / +│ # ERC1643MissingDocument); NO access-control specifics ├── interfaces/ -│ └── IERC8303.sol # ERC-8303 "Contract Version" interface (id 0x54fd4d50) +│ ├── IERC8303.sol # ERC-8303 "Contract Version" interface (id 0x54fd4d50) +│ └── IERC1643MultiDocument.sol # Multi-token ERC-1643 extension (address-scoped fns + +│ # DocumentUpdatedForSubject / DocumentRemovedForSubject) └── modules/ └── VersionModule.sol # Version module: implements ERC-8303 version() + ERC-165, # holds the VERSION constant (currently "0.4.0") diff --git a/README.md b/README.md index be739cc..3862f4c 100644 --- a/README.md +++ b/README.md @@ -127,12 +127,17 @@ as a reference for that pattern. ### Events -On every write, the engine emits the standard `IERC1643` events **and** the -optional `DocumentUpdatedForContract` / `DocumentRemovedForContract` events, -which additionally carry the `smartContract` (token) address so off-chain -indexers can tell which contract a document belongs to during multi-contract -operations. See [ERC-1643-proposition.md](./ERC-1643-proposition.md) for the -proposed optional standard extension. +This engine is a **shared, multi-token** document manager, so — per the ERC-1643 +["Emission Responsibility"](./doc/ERCSpecification/erc-1643.md) rules — it emits +**only** the address-carrying extension events +`DocumentUpdatedForSubject(address indexed subject, …)` / +`DocumentRemovedForSubject(…)`, and **not** the base `DocumentUpdated` / +`DocumentRemoved` events. The base events carry no address and so cannot identify +which token contract a change belongs to; they are the responsibility of the +token contract that exposes ERC-1643 to consumers (it re-emits them when +delegating). See +[ERC-1643-proposition.md](./doc/ERCSpecification/ERC-1643-proposition.md) and the +`IERC1643MultiDocument` extension. ### Integration with CMTAT diff --git a/doc/ERCSpecification/ERC-1643-proposition.md b/doc/ERCSpecification/ERC-1643-proposition.md deleted file mode 100644 index e6b57df..0000000 --- a/doc/ERCSpecification/ERC-1643-proposition.md +++ /dev/null @@ -1,129 +0,0 @@ -# ERC-1643 — Proposition: optional multi-token document management - -> Status: **draft / proposition**. This document proposes optional additions to -> [ERC-1643](https://github.com/ethereum/EIPs/issues/1643) motivated by the -> `DocumentEngine` implementation in this repository. It is not part of the -> standard and is provided for discussion. - -## Context - -ERC-1643 describes document management **for a single contract**: the token that -implements the interface manages its own documents. In CMTAT v3, the standard -interface is: - -```solidity -interface IERC1643 { - struct Document { - string uri; - bytes32 documentHash; - uint256 lastModified; - } - - function getDocument(bytes32 name) external view returns (Document memory document); - function getAllDocuments() external view returns (bytes32[] memory documentNames_); - function setDocument(bytes32 name, string calldata uri, bytes32 documentHash) external; - function removeDocument(bytes32 name) external; - - event DocumentUpdated(bytes32 indexed name, string uri, bytes32 documentHash); - event DocumentRemoved(bytes32 indexed name, string uri, bytes32 documentHash); -} -``` - -A **document engine** is a different use case: a single external contract manages -documents **on behalf of several tokens**, keyed by the token address. This -reduces the code size of each token and lets one operator manage documents for a -whole fleet of tokens. - -For that multi-token use case, the standard events are **insufficient**: they only -carry the document `name`. When a single transaction sets documents for several -different tokens (batch operations), an off-chain indexer cannot tell from the -event alone which token a document belongs to — the emitting contract is always -the engine, not the token. - -## Proposition 1 — Optional multi-token events - -We propose two **optional** events that mirror the standard ones but add the -`smartContract` (token) address: - -```solidity -event DocumentUpdatedForContract( - address indexed smartContract, - bytes32 indexed name, - string uri, - bytes32 documentHash -); - -event DocumentRemovedForContract( - address indexed smartContract, - bytes32 indexed name, - string uri, - bytes32 documentHash -); -``` - -An engine that manages documents for several tokens SHOULD emit these events in -addition to the standard `DocumentUpdated` / `DocumentRemoved` events. The -standard events remain emitted for full backward compatibility with consumers -that only understand ERC-1643. - -### Important limitation: no ERC-165 discoverability - -Events are **not** part of a contract's ERC-165 interface id — `supportsInterface` -only covers function selectors. Consequently **there is no on-chain way for a -consumer to know whether a given implementation emits these optional events.** -A consumer that relies on them must obtain that information out of band (e.g. -documentation, a known implementation, or the optional extension interface below). - -## Proposition 2 — Optional multi-token management extension - -For implementations that want the multi-token capability to be -programmatically discoverable (via ERC-165) and callable, we propose an optional -extension interface. Unlike the events above, function selectors **are** part of -the ERC-165 interface id, so support can be detected on-chain. - -```solidity -interface IERC1643MultiDocument is IERC1643 { - /// @notice Get a document registered for `smartContract`. - function getDocument(address smartContract, bytes32 name) - external view returns (Document memory document); - - /// @notice Get all document names registered for `smartContract`. - function getAllDocuments(address smartContract) - external view returns (bytes32[] memory documentNames_); - - /// @notice Set or update a document for `smartContract`. - function setDocument(address smartContract, bytes32 name, string calldata uri, bytes32 documentHash) - external; - - /// @notice Remove a document for `smartContract`. - function removeDocument(address smartContract, bytes32 name) - external; - - event DocumentUpdatedForContract( - address indexed smartContract, - bytes32 indexed name, - string uri, - bytes32 documentHash - ); - event DocumentRemovedForContract( - address indexed smartContract, - bytes32 indexed name, - string uri, - bytes32 documentHash - ); -} -``` - -The single-argument functions inherited from `IERC1643` keep their standard -meaning: they operate on the caller (`msg.sender`) namespace, i.e. a token -managing its own documents. The address-scoped functions add the operator / -multi-token capability. - -## How this repository implements the proposition - -The `DocumentEngine` in this repository already follows Proposition 1: every -write emits both the standard event and the `...ForContract` variant. It also -provides the address-scoped functions of Proposition 2 (under -`DOCUMENT_MANAGER_ROLE`) and the single-argument, `msg.sender`-scoped functions -(under `TOKEN_CONTRACT_ROLE`, the RuleEngine binding pattern), though it does not -yet formally declare/expose an `IERC1643MultiDocument` interface id. diff --git a/doc/ERCSpecification/erc-1643.md b/doc/ERCSpecification/erc-1643.md index 20cc073..28f63a7 100644 --- a/doc/ERCSpecification/erc-1643.md +++ b/doc/ERCSpecification/erc-1643.md @@ -30,7 +30,7 @@ Historically, this proposal was authored as part of a broader security token sta ## Specification -The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", and "MAY" in this document are to be interpreted as described in RFC 2119 and RFC 8174. +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. Implementations MUST support querying and subscribing to updates on any relevant documentation for the security. @@ -82,6 +82,13 @@ Implementations SHOULD support ERC-165 interface detection. When ERC-165 is implemented, `supportsInterface` SHOULD return `true` for `type(IERC1643).interfaceId` and for the ERC-165 interface id. +`type(IERC1643MultiDocument).interfaceId` is the XOR of only the extension's own address-scoped functions; per ERC-165 semantics it does not include the base `IERC1643` selectors. Advertising the extension id therefore says nothing about whether the base single-argument functions are implemented. Accordingly: + +- A contract that implements the extension SHOULD return `true` for `type(IERC1643MultiDocument).interfaceId`. +- A contract MUST return `true` for `type(IERC1643).interfaceId` only if it also implements the base single-argument functions. A shared management contract that exposes only the address-scoped surface MUST NOT advertise `type(IERC1643).interfaceId`, since it does not implement that interface. + +Events are not part of any ERC-165 interface id, so `supportsInterface` reflects only which functions a contract implements, not whether it emits the base or extension events. + ### Function Requirements - `getDocument`: @@ -112,6 +119,58 @@ When ERC-165 is implemented, `supportsInterface` SHOULD return `true` for `type( - MUST include every document name added by `setDocument` and not removed by `removeDocument`. - MUST NOT include removed document names. +### Optional Extension: Multi-Token Document Management + +This extension is **not part of the original [ERC-1643](./eip-1643.md)**; it is an optional, additive addition for the case where document management is **delegated to a separate smart contract** that serves **more than one** token contract. + +The base `DocumentUpdated` / `DocumentRemoved` events carry no address, so consumers attribute them to the address of the contract that emitted them. This is unambiguous when a single contract both exposes ERC-1643 and stores its own documents, but breaks when one shared contract manages documents for several token contracts: the base events cannot identify which token contract a change belongs to. The extension adds address-scoped functions and address-carrying events, where `subject` is the address of the contract the documents belong to (typically a token contract, but the reasoning applies to any ERC-721/ERC-1155 token, vault, or other on-chain product). + +The extension is intentionally declared **independently of `IERC1643`** (it does not inherit it), so a shared management contract can implement the address-scoped surface **without** being forced to implement the base single-argument functions. A contract MAY implement both interfaces — for example a manager that also lets a token contract manage its own documents by calling the base functions with `msg.sender` as the implied subject — in which case it implements, and advertises, each interface separately. + +```solidity +/// @title IERC1643MultiDocument Multi-Token Document Management (optional extension) +interface IERC1643MultiDocument { + /// @notice Returns metadata for the document identified by `name` belonging to `subject`. + /// @param subject Address of the contract the documents belong to. + /// @return uri Document location. + /// @return documentHash Hash of the document contents. + /// @return lastModified Last update timestamp. + function getDocument(address subject, bytes32 name) external view returns (string memory uri, bytes32 documentHash, uint256 lastModified); + + /// @notice Creates or updates a document entry for `subject`. + /// @dev MUST emit `DocumentUpdatedForSubject` on success. + function setDocument(address subject, bytes32 name, string calldata uri, bytes32 documentHash) external; + + /// @notice Removes an existing document entry for `subject`. + /// @dev MUST emit `DocumentRemovedForSubject` on success. + function removeDocument(address subject, bytes32 name) external; + + /// @notice Returns all document names currently tracked for `subject`. + function getAllDocuments(address subject) external view returns (bytes32[] memory documentNames); + + /// @notice Emitted when a document is created or updated for `subject`. + event DocumentUpdatedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); + + /// @notice Emitted when a document is removed for `subject`. + event DocumentRemovedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); +} +``` + +#### Emission Responsibility + +The base events MUST be emitted by the contract that exposes ERC-1643 to consumers — the address consumers are expected to subscribe to. When document management is delegated, the emitter MUST be chosen so per-contract observability is preserved: + +- A management contract **dedicated to a single** token contract MAY be that token contract's ERC-1643 implementation and MUST emit the base `DocumentUpdated` / `DocumentRemoved` events; the base events are unambiguous because only one subject is served. +- A management contract **shared by multiple** token contracts MUST NOT report per-subject changes through the base events, since those events cannot identify the subject. In this configuration each token contract MUST emit the base events for its own documents, and the shared contract SHOULD emit the extension events (`DocumentUpdatedForSubject` / `DocumentRemovedForSubject`) instead, as those carry the `subject` address. +- Implementations SHOULD NOT emit the base events from **both** the token contract and the management contract. + +#### Extension Function Requirements + +- `getDocument(address subject, bytes32 name)` and `getAllDocuments(address subject)`: behave as their base counterparts, scoped to `subject`. +- `setDocument(address subject, ...)`: MUST emit `DocumentUpdatedForSubject` after state changes; SHOULD revert with `ERC1643InvalidName()` when `name == bytes32(0)`. +- `removeDocument(address subject, bytes32 name)`: MUST emit `DocumentRemovedForSubject`; SHOULD revert with `ERC1643MissingDocument()` when the named document does not exist for `subject`. +- Implementations MUST authorize writes per `subject`, so that a caller cannot create, update, or remove documents for a `subject` it is not permitted to manage. + ## Rationale The standard uses `bytes32` names to keep keys compact and deterministic, while leaving naming conventions to implementations. A URI-based pointer is used instead of on-chain document storage to avoid high gas costs and to support existing off-chain document systems. @@ -124,6 +183,8 @@ While a human-readable document title cannot always be represented directly in ` This ERC is additive and does not alter base token transfer semantics. It can be implemented alongside existing token standards and permissioning systems without changing their core behavior. +The optional multi-token extension (`IERC1643MultiDocument`) is likewise additive and was not part of the original ERC-1643. Its address-scoped functions have different signatures — hence different selectors — than the base functions, so they sit alongside them without collision, and its `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` events are new topics that leave the base `DocumentUpdated` / `DocumentRemoved` events unchanged. A consumer that only knows base ERC-1643 is unaffected: it continues to call the base functions and subscribe to the base events on the address it was directed to watch. As required in the Specification, an implementation of the extension leaves the base functions and events unchanged, and every address a consumer is directed to subscribe to still emits the base events. + ## Test Cases Implementations should verify at least the following: @@ -152,6 +213,7 @@ The module maintains: - Document names may not always fit cleanly into `bytes32`, especially for long legal titles. Implementations should avoid lossy truncation of human-readable names; using a deterministic hash-based identifier (for example, the document content hash or a hash of a canonical full title) as the `bytes32` name is a safer alternative. - The custom errors `ERC1643InvalidName()` and `ERC1643MissingDocument()` are defined in this interface but were absent from the original [ERC-1643](./eip-1643.md) proposal text. Older implementations may not define these errors and may instead revert with strings or implementation-specific error patterns. Integrators should not assume all ERC-1643 contracts expose identical revert data. - ERC-165 interface detection was also not part of the earlier ERC-1643 draft text. Older implementations may not expose `supportsInterface` for ERC-165 or `IERC1643`, so integrators should treat ERC-165 support as optional when interacting with legacy deployments. +- A shared document-management contract implementing the optional multi-token extension governs documents for several subjects behind a single address. It has to authorize writes per `subject` (as required in the Specification); otherwise a caller could modify another subject's legal or operational references. Because events are not covered by ERC-165, `supportsInterface(type(IERC1643MultiDocument).interfaceId)` confirms only that the extension functions exist, not that the address-carrying events are actually emitted — integrators relying on the extension events should confirm emission out of band. ## Copyright diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index d1d16ff..b837127 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.20; import "OZ/access/extensions/AccessControlEnumerable.sol"; import {IAccessControl} from "OZ/access/IAccessControl.sol"; +import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; +import {IERC1643MultiDocument} from "./interfaces/IERC1643MultiDocument.sol"; import "OZ/metatx/ERC2771Context.sol"; import "./DocumentEngineBase.sol"; import "./modules/VersionModule.sol"; @@ -86,8 +88,12 @@ contract DocumentEngine is } /** - * @dev Combines the ERC-165 interface discovery of the version module - * (ERC-8303) with `AccessControlEnumerable`. See {IERC165-supportsInterface}. + * @dev ERC-165 discovery: advertises ERC-1643 and its multi-token extension, + * plus the version module (ERC-8303) and `AccessControlEnumerable`. + * The engine implements the base single-argument functions, so it advertises + * `type(IERC1643).interfaceId`; it also implements the address-scoped + * extension, so it advertises `type(IERC1643MultiDocument).interfaceId`. + * See {IERC165-supportsInterface}. */ function supportsInterface( bytes4 interfaceId @@ -98,7 +104,10 @@ contract DocumentEngine is override(VersionModule, AccessControlEnumerable) returns (bool) { - return super.supportsInterface(interfaceId); + return + interfaceId == type(IERC1643).interfaceId || + interfaceId == type(IERC1643MultiDocument).interfaceId || + super.supportsInterface(interfaceId); } /*////////////////////////////////////////////////////////////// diff --git a/src/DocumentEngineBase.sol b/src/DocumentEngineBase.sol index 0e61b4c..b17cf8f 100644 --- a/src/DocumentEngineBase.sol +++ b/src/DocumentEngineBase.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.20; import "OZ/utils/Context.sol"; import "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; +import {IERC1643MultiDocument} from "./interfaces/IERC1643MultiDocument.sol"; import "./DocumentEngineInvariant.sol"; /** @@ -20,6 +21,7 @@ import "./DocumentEngineInvariant.sol"; */ abstract contract DocumentEngineBase is IERC1643, + IERC1643MultiDocument, DocumentEngineInvariant, Context { @@ -72,22 +74,22 @@ abstract contract DocumentEngineBase is * @notice Restricted function to set or update a document */ function setDocument( - address smartContract, + address subject, bytes32 name_, string memory uri_, bytes32 documentHash_ - ) public onlyDocumentManager { - _setDocument(smartContract, name_, uri_, documentHash_); + ) public override onlyDocumentManager { + _setDocument(subject, name_, uri_, documentHash_); } /** * @notice Restricted function to remove a document for a given smart contract and name */ function removeDocument( - address smartContract, + address subject, bytes32 name_ - ) external onlyDocumentManager { - _removeDocument(smartContract, name_); + ) external override onlyDocumentManager { + _removeDocument(subject, name_); } /* ============ ERC-1643 (bound token) ============ */ @@ -208,10 +210,10 @@ abstract contract DocumentEngineBase is * @notice Public function to get a document for a specific contract address */ function getDocument( - address smartContract, + address subject, bytes32 name_ - ) external view returns (Document memory) { - return _getDocument(smartContract, name_); + ) external view override returns (Document memory) { + return _getDocument(subject, name_); } /** @@ -230,9 +232,9 @@ abstract contract DocumentEngineBase is * @notice Get all document names for a specific smart contract */ function getAllDocuments( - address smartContract - ) external view returns (bytes32[] memory) { - return _documentNames[smartContract]; + address subject + ) external view override returns (bytes32[] memory) { + return _documentNames[subject]; } /*////////////////////////////////////////////////////////////// @@ -268,44 +270,50 @@ abstract contract DocumentEngineBase is } } - function _removeDocument(address smartContract, bytes32 name_) internal { - Document memory doc = _documents[smartContract][name_]; - // Standard ERC-1643 event - emit DocumentRemoved(name_, doc.uri, doc.documentHash); - // Optional multi-token event (see ERC-1643-proposition.md) - emit DocumentRemovedForContract( - smartContract, + function _removeDocument(address subject, bytes32 name_) internal { + Document memory doc = _documents[subject][name_]; + // ERC-1643: reverts when the named document does not exist + if (doc.lastModified == 0) { + revert ERC1643MissingDocument(); + } + + // This engine is a shared, multi-subject manager: per the ERC-1643 + // "Emission Responsibility" rules it emits only the address-carrying + // extension event (the base `DocumentRemoved` is the token contract's + // responsibility). See doc/ERCSpecification. + emit DocumentRemovedForSubject( + subject, name_, doc.uri, doc.documentHash ); - delete _documents[smartContract][name_]; - _removeDocumentName(smartContract, name_); + delete _documents[subject][name_]; + _removeDocumentName(subject, name_); } function _setDocument( - address smartContract, + address subject, bytes32 name_, string memory uri_, bytes32 documentHash_ ) internal { - Document storage doc = _documents[smartContract][name_]; + // ERC-1643: reject the null name (ambiguous / default key) + if (name_ == bytes32(0)) { + revert ERC1643InvalidName(); + } + + Document storage doc = _documents[subject][name_]; if (doc.lastModified == 0) { // new document - _documentNames[smartContract].push(name_); + _documentNames[subject].push(name_); } doc.uri = uri_; doc.documentHash = documentHash_; doc.lastModified = block.timestamp; - // Standard ERC-1643 event - emit DocumentUpdated(name_, uri_, documentHash_); - // Optional multi-token event (see ERC-1643-proposition.md) - emit DocumentUpdatedForContract( - smartContract, - name_, - uri_, - documentHash_ - ); + + // Shared, multi-subject manager: emit only the address-carrying extension + // event (see {_removeDocument} note and doc/ERCSpecification). + emit DocumentUpdatedForSubject(subject, name_, uri_, documentHash_); } } diff --git a/src/DocumentEngineInvariant.sol b/src/DocumentEngineInvariant.sol index a554bfd..42e27a0 100644 --- a/src/DocumentEngineInvariant.sol +++ b/src/DocumentEngineInvariant.sol @@ -14,25 +14,11 @@ contract DocumentEngineInvariant { error InvalidInputLength(); error AdminWithAddressZeroNotAllowed(); - /** - * @notice Optional multi-token events emitted in addition to the standard - * `IERC1643.DocumentUpdated` / `IERC1643.DocumentRemoved` events. - * @dev Because this engine manages documents on behalf of several smart - * contracts (tokens), the standard events - which only carry the document - * `name` - are not sufficient to identify which contract a document belongs - * to. These events add the `smartContract` address for off-chain indexers. - * See `ERC-1643-proposition.md` for the proposed optional standard extension. - */ - event DocumentUpdatedForContract( - address indexed smartContract, - bytes32 indexed name, - string uri, - bytes32 documentHash - ); - event DocumentRemovedForContract( - address indexed smartContract, - bytes32 indexed name, - string uri, - bytes32 documentHash - ); + /// @notice Reverts when `setDocument` is called with `name == bytes32(0)`. + /// @dev ERC-1643-recommended error name. + error ERC1643InvalidName(); + + /// @notice Reverts when `removeDocument` targets a document that does not exist. + /// @dev ERC-1643-recommended error name. + error ERC1643MissingDocument(); } diff --git a/src/DocumentEngineOwnable.sol b/src/DocumentEngineOwnable.sol index 976ea3c..b221e9d 100644 --- a/src/DocumentEngineOwnable.sol +++ b/src/DocumentEngineOwnable.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.20; import {Ownable} from "OZ/access/Ownable.sol"; import {Ownable2Step} from "OZ/access/Ownable2Step.sol"; +import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; +import {IERC1643MultiDocument} from "./interfaces/IERC1643MultiDocument.sol"; import "OZ/metatx/ERC2771Context.sol"; import "./DocumentEngineBase.sol"; import "./modules/VersionModule.sol"; @@ -89,6 +91,19 @@ contract DocumentEngineOwnable is } } + /** + * @dev ERC-165 discovery: advertises ERC-1643 and its multi-token extension, + * plus the version module (ERC-8303). See {IERC165-supportsInterface}. + */ + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(VersionModule) returns (bool) { + return + interfaceId == type(IERC1643).interfaceId || + interfaceId == type(IERC1643MultiDocument).interfaceId || + super.supportsInterface(interfaceId); + } + /*////////////////////////////////////////////////////////////// ERC2771 //////////////////////////////////////////////////////////////*/ diff --git a/src/interfaces/IERC1643MultiDocument.sol b/src/interfaces/IERC1643MultiDocument.sol new file mode 100644 index 0000000..2173ef4 --- /dev/null +++ b/src/interfaces/IERC1643MultiDocument.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.20; + +import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; + +/** + * @title IERC1643MultiDocument — optional multi-token ERC-1643 extension + * @notice Address-scoped document management for a contract that manages + * documents on behalf of several `subject` contracts. + * @dev Declared **independently of `IERC1643`** (it does not inherit it), so a + * shared management contract can implement the address-scoped surface without + * being forced to implement the base single-argument functions. `subject` is the + * address of the contract the documents belong to (typically a token contract, + * but the reasoning applies to any ERC-721/ERC-1155 token, vault, or other + * on-chain product). See `doc/ERCSpecification/ERC-1643-proposition.md`. + */ +interface IERC1643MultiDocument { + /// @notice Returns metadata for the document `name` belonging to `subject`. + function getDocument( + address subject, + bytes32 name + ) external view returns (IERC1643.Document memory document); + + /// @notice Returns all document names currently tracked for `subject`. + function getAllDocuments( + address subject + ) external view returns (bytes32[] memory documentNames); + + /// @notice Creates or updates a document entry for `subject`. + /// @dev MUST emit {DocumentUpdatedForSubject} on success. + function setDocument( + address subject, + bytes32 name, + string calldata uri, + bytes32 documentHash + ) external; + + /// @notice Removes an existing document entry for `subject`. + /// @dev MUST emit {DocumentRemovedForSubject} on success. + function removeDocument(address subject, bytes32 name) external; + + /// @notice Emitted when a document is created or updated for `subject`. + event DocumentUpdatedForSubject( + address indexed subject, + bytes32 indexed name, + string uri, + bytes32 documentHash + ); + + /// @notice Emitted when a document is removed for `subject`. + event DocumentRemovedForSubject( + address indexed subject, + bytes32 indexed name, + string uri, + bytes32 documentHash + ); +} diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index fdebd21..77de7f3 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -7,6 +7,7 @@ import "../src/DocumentEngineInvariant.sol"; import "OZ/access/AccessControl.sol"; import {IERC165} from "OZ/utils/introspection/IERC165.sol"; import {IERC8303} from "../src/interfaces/IERC8303.sol"; +import {IERC1643MultiDocument} from "../src/interfaces/IERC1643MultiDocument.sol"; import {DocumentEngineModule} from "CMTAT/modules/wrapper/options/DocumentEngineModule.sol"; /** @@ -334,6 +335,52 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { ); } + function testSupportsERC1643Interfaces() public { + // implements the base single-argument functions... + assertTrue(documentEngine.supportsInterface(type(IERC1643).interfaceId)); + // ...and the address-scoped multi-token extension + assertTrue( + documentEngine.supportsInterface( + type(IERC1643MultiDocument).interfaceId + ) + ); + } + + /*////////////////////////////////////////////////////////////// + ERC-1643 input validation + //////////////////////////////////////////////////////////////*/ + + function testCannotSetDocumentWithZeroName() public { + vm.prank(admin); + vm.expectRevert( + abi.encodeWithSelector(ERC1643InvalidName.selector) + ); + documentEngine.setDocument( + testContract, + bytes32(0), + documentURI, + documentHash + ); + } + + function testCannotRemoveMissingDocument() public { + vm.prank(admin); + vm.expectRevert( + abi.encodeWithSelector(ERC1643MissingDocument.selector) + ); + documentEngine.removeDocument(testContract, keccak256("does-not-exist")); + } + + function testBoundTokenCannotSetZeroName() public { + vm.prank(admin); + documentEngine.grantRole(TOKEN_CONTRACT_ROLE, testContract); + vm.prank(testContract); + vm.expectRevert( + abi.encodeWithSelector(ERC1643InvalidName.selector) + ); + documentEngine.setDocument(bytes32(0), documentURI, documentHash); + } + function testSupportsInterfaceERC165AndAccessControl() public { assertTrue( documentEngine.supportsInterface(type(IERC165).interfaceId) From 0bf09e9dd6a61f7f9fdc2e146d6980602d4a324e Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 13:33:02 +0200 Subject: [PATCH 10/47] feat: add Foundry deployment scripts for both DocumentEngine variants, with tests --- AGENTS.md | 10 ++- CLAUDE.md | 10 ++- README.md | 22 ++++++- script/DeployDocumentEngine.s.sol | 40 ++++++++++++ script/DeployDocumentEngineOwnable.s.sol | 43 +++++++++++++ test/Deploy.t.sol | 78 ++++++++++++++++++++++++ 6 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 script/DeployDocumentEngine.s.sol create mode 100644 script/DeployDocumentEngineOwnable.s.sol create mode 100644 test/Deploy.t.sol diff --git a/AGENTS.md b/AGENTS.md index 6d4f5ca..3176959 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -78,13 +78,19 @@ src/ └── VersionModule.sol # Version module: implements ERC-8303 version() + ERC-165, # holds the VERSION constant (currently "0.4.0") +script/ +├── DeployDocumentEngine.s.sol # Deploy role-based DocumentEngine (env: DOCUMENT_ENGINE_ADMIN, +│ # DOCUMENT_ENGINE_FORWARDER); run()=env, deploy(admin,fwd)=testable +└── DeployDocumentEngineOwnable.s.sol # Deploy Ownable variant (env: DOCUMENT_ENGINE_OWNER, _FORWARDER) + test/ ├── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin path, │ # bound-token path, batch ops, ERC-8303, CMTAT integration │ # (CMTATDocumentEngineMock built on DocumentEngineModule), │ # flexible-authorization override (OpenDocumentEngine) -└── DocumentEngineOwnable.t.sol # Tests for the Ownable2Step deployment (owner path, - # token binding, two-step ownership, ERC-8303) +├── DocumentEngineOwnable.t.sol # Tests for the Ownable2Step deployment (owner path, +│ # token binding, two-step ownership, ERC-8303) +└── Deploy.t.sol # Tests for the deployment scripts (deploy() state + run() env) ``` **Contract split (CMTAT module/deployment pattern):** `DocumentEngineBase` holds diff --git a/CLAUDE.md b/CLAUDE.md index 6d4f5ca..3176959 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -78,13 +78,19 @@ src/ └── VersionModule.sol # Version module: implements ERC-8303 version() + ERC-165, # holds the VERSION constant (currently "0.4.0") +script/ +├── DeployDocumentEngine.s.sol # Deploy role-based DocumentEngine (env: DOCUMENT_ENGINE_ADMIN, +│ # DOCUMENT_ENGINE_FORWARDER); run()=env, deploy(admin,fwd)=testable +└── DeployDocumentEngineOwnable.s.sol # Deploy Ownable variant (env: DOCUMENT_ENGINE_OWNER, _FORWARDER) + test/ ├── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin path, │ # bound-token path, batch ops, ERC-8303, CMTAT integration │ # (CMTATDocumentEngineMock built on DocumentEngineModule), │ # flexible-authorization override (OpenDocumentEngine) -└── DocumentEngineOwnable.t.sol # Tests for the Ownable2Step deployment (owner path, - # token binding, two-step ownership, ERC-8303) +├── DocumentEngineOwnable.t.sol # Tests for the Ownable2Step deployment (owner path, +│ # token binding, two-step ownership, ERC-8303) +└── Deploy.t.sol # Tests for the deployment scripts (deploy() state + run() env) ``` **Contract split (CMTAT module/deployment pattern):** `DocumentEngineBase` holds diff --git a/README.md b/README.md index 3862f4c..7df3c11 100644 --- a/README.md +++ b/README.md @@ -350,10 +350,30 @@ $ anvil ##### Deploy +Two deployment scripts are provided in [`script/`](./script), one per access-control +variant. Both read their configuration from environment variables: + +| Variable | Used by | Default | Meaning | +| --- | --- | --- | --- | +| `DOCUMENT_ENGINE_ADMIN` | `DeployDocumentEngine` | `msg.sender` | account granted `DEFAULT_ADMIN_ROLE` | +| `DOCUMENT_ENGINE_OWNER` | `DeployDocumentEngineOwnable` | `msg.sender` | initial owner | +| `DOCUMENT_ENGINE_FORWARDER` | both | `address(0)` | ERC-2771 trusted forwarder (`address(0)` disables gasless) | + ```shell -$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +# Role-based DocumentEngine (AccessControlEnumerable) +$ DOCUMENT_ENGINE_ADMIN=0xYourAdmin \ + forge script script/DeployDocumentEngine.s.sol \ + --rpc-url --private-key --broadcast + +# Owner-based DocumentEngineOwnable (Ownable2Step) +$ DOCUMENT_ENGINE_OWNER=0xYourOwner \ + forge script script/DeployDocumentEngineOwnable.s.sol \ + --rpc-url --private-key --broadcast ``` +Drop `--broadcast` (and `--rpc-url`) for a local dry-run. The scripts are covered by +[`test/Deploy.t.sol`](./test/Deploy.t.sol). + ##### Cast ```shell diff --git a/script/DeployDocumentEngine.s.sol b/script/DeployDocumentEngine.s.sol new file mode 100644 index 0000000..48e983a --- /dev/null +++ b/script/DeployDocumentEngine.s.sol @@ -0,0 +1,40 @@ +//SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.20; + +import "forge-std/Script.sol"; +import {DocumentEngine} from "../src/DocumentEngine.sol"; + +/** + * @title DeployDocumentEngine + * @notice Deploys the role-based {DocumentEngine} (AccessControlEnumerable). + * @dev Configuration via environment variables: + * - `DOCUMENT_ENGINE_ADMIN` : address granted `DEFAULT_ADMIN_ROLE` (default: `msg.sender`) + * - `DOCUMENT_ENGINE_FORWARDER` : ERC-2771 trusted forwarder, `address(0)` disables gasless (default: `address(0)`) + * + * Usage: + * forge script script/DeployDocumentEngine.s.sol \ + * --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast + */ +contract DeployDocumentEngine is Script { + function run() external returns (DocumentEngine documentEngine) { + address admin = vm.envOr("DOCUMENT_ENGINE_ADMIN", msg.sender); + address forwarder = vm.envOr("DOCUMENT_ENGINE_FORWARDER", address(0)); + + documentEngine = deploy(admin, forwarder); + + console2.log("DocumentEngine deployed at:", address(documentEngine)); + console2.log(" admin :", admin); + console2.log(" trusted forwarder:", forwarder); + console2.log(" version :", documentEngine.version()); + } + + /// @dev Broadcasted deployment, isolated from env parsing so it can be reused/tested. + function deploy( + address admin, + address forwarder + ) public returns (DocumentEngine documentEngine) { + vm.startBroadcast(); + documentEngine = new DocumentEngine(admin, forwarder); + vm.stopBroadcast(); + } +} diff --git a/script/DeployDocumentEngineOwnable.s.sol b/script/DeployDocumentEngineOwnable.s.sol new file mode 100644 index 0000000..e76c813 --- /dev/null +++ b/script/DeployDocumentEngineOwnable.s.sol @@ -0,0 +1,43 @@ +//SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.20; + +import "forge-std/Script.sol"; +import {DocumentEngineOwnable} from "../src/DocumentEngineOwnable.sol"; + +/** + * @title DeployDocumentEngineOwnable + * @notice Deploys the owner-based {DocumentEngineOwnable} (Ownable2Step). + * @dev Configuration via environment variables: + * - `DOCUMENT_ENGINE_OWNER` : initial owner (default: `msg.sender`) + * - `DOCUMENT_ENGINE_FORWARDER` : ERC-2771 trusted forwarder, `address(0)` disables gasless (default: `address(0)`) + * + * Usage: + * forge script script/DeployDocumentEngineOwnable.s.sol \ + * --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast + */ +contract DeployDocumentEngineOwnable is Script { + function run() external returns (DocumentEngineOwnable documentEngine) { + address owner = vm.envOr("DOCUMENT_ENGINE_OWNER", msg.sender); + address forwarder = vm.envOr("DOCUMENT_ENGINE_FORWARDER", address(0)); + + documentEngine = deploy(owner, forwarder); + + console2.log( + "DocumentEngineOwnable deployed at:", + address(documentEngine) + ); + console2.log(" owner :", owner); + console2.log(" trusted forwarder:", forwarder); + console2.log(" version :", documentEngine.version()); + } + + /// @dev Broadcasted deployment, isolated from env parsing so it can be reused/tested. + function deploy( + address owner, + address forwarder + ) public returns (DocumentEngineOwnable documentEngine) { + vm.startBroadcast(); + documentEngine = new DocumentEngineOwnable(owner, forwarder); + vm.stopBroadcast(); + } +} diff --git a/test/Deploy.t.sol b/test/Deploy.t.sol new file mode 100644 index 0000000..815f1cd --- /dev/null +++ b/test/Deploy.t.sol @@ -0,0 +1,78 @@ +//SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.20; + +import "forge-std/Test.sol"; +import {DeployDocumentEngine} from "../script/DeployDocumentEngine.s.sol"; +import {DeployDocumentEngineOwnable} from "../script/DeployDocumentEngineOwnable.s.sol"; +import {DocumentEngine} from "../src/DocumentEngine.sol"; +import {DocumentEngineOwnable} from "../src/DocumentEngineOwnable.sol"; + +contract DeployDocumentEngineTest is Test { + DeployDocumentEngine internal deployer; + address internal admin = makeAddr("admin"); + address internal forwarder = makeAddr("forwarder"); + + function setUp() public { + deployer = new DeployDocumentEngine(); + } + + function testDeploySetsAdminAndForwarder() public { + DocumentEngine engine = deployer.deploy(admin, forwarder); + + assertTrue(engine.hasRole(engine.DEFAULT_ADMIN_ROLE(), admin)); + assertTrue(engine.isTrustedForwarder(forwarder)); + assertEq(engine.version(), "0.4.0"); + } + + function testDeployWithoutForwarder() public { + DocumentEngine engine = deployer.deploy(admin, address(0)); + + assertTrue(engine.hasRole(engine.DEFAULT_ADMIN_ROLE(), admin)); + assertFalse(engine.isTrustedForwarder(forwarder)); + } + + function testRunReadsEnv() public { + vm.setEnv("DOCUMENT_ENGINE_ADMIN", vm.toString(admin)); + vm.setEnv("DOCUMENT_ENGINE_FORWARDER", vm.toString(forwarder)); + + DocumentEngine engine = deployer.run(); + + assertTrue(engine.hasRole(engine.DEFAULT_ADMIN_ROLE(), admin)); + assertTrue(engine.isTrustedForwarder(forwarder)); + } +} + +contract DeployDocumentEngineOwnableTest is Test { + DeployDocumentEngineOwnable internal deployer; + address internal owner = makeAddr("owner"); + address internal forwarder = makeAddr("forwarder"); + + function setUp() public { + deployer = new DeployDocumentEngineOwnable(); + } + + function testDeploySetsOwnerAndForwarder() public { + DocumentEngineOwnable engine = deployer.deploy(owner, forwarder); + + assertEq(engine.owner(), owner); + assertTrue(engine.isTrustedForwarder(forwarder)); + assertEq(engine.version(), "0.4.0"); + } + + function testDeployWithoutForwarder() public { + DocumentEngineOwnable engine = deployer.deploy(owner, address(0)); + + assertEq(engine.owner(), owner); + assertFalse(engine.isTrustedForwarder(forwarder)); + } + + function testRunReadsEnv() public { + vm.setEnv("DOCUMENT_ENGINE_OWNER", vm.toString(owner)); + vm.setEnv("DOCUMENT_ENGINE_FORWARDER", vm.toString(forwarder)); + + DocumentEngineOwnable engine = deployer.run(); + + assertEq(engine.owner(), owner); + assertTrue(engine.isTrustedForwarder(forwarder)); + } +} From 2e3e114c6ddd657af57c9bdc147dee3485d465ef Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 13:35:37 +0200 Subject: [PATCH 11/47] Environment Variables Don't use it for production, only for testing. See getfoundry.sh - Key Management to securely broadcasting transactions through a script --- README.md | 10 ++++++++++ script/DeployDocumentEngine.s.sol | 5 +++++ script/DeployDocumentEngineOwnable.s.sol | 5 +++++ 3 files changed, 20 insertions(+) diff --git a/README.md b/README.md index 7df3c11..ddba5bd 100644 --- a/README.md +++ b/README.md @@ -359,6 +359,16 @@ variant. Both read their configuration from environment variables: | `DOCUMENT_ENGINE_OWNER` | `DeployDocumentEngineOwnable` | `msg.sender` | initial owner | | `DOCUMENT_ENGINE_FORWARDER` | both | `address(0)` | ERC-2771 trusted forwarder (`address(0)` disables gasless) | +> **Warning** +> +> These environment variables, and passing a raw key with `--private-key`, are +> intended for **local testing only — do not use them in production**. A private +> key supplied on the command line or through an environment variable is exposed +> in your shell history and process environment. For production deployments, use a +> secure signing method (encrypted keystore, hardware wallet, ...) as described in +> the Foundry Key Management documentation (getfoundry.sh) for securely +> broadcasting transactions through a script. + ```shell # Role-based DocumentEngine (AccessControlEnumerable) $ DOCUMENT_ENGINE_ADMIN=0xYourAdmin \ diff --git a/script/DeployDocumentEngine.s.sol b/script/DeployDocumentEngine.s.sol index 48e983a..b778daf 100644 --- a/script/DeployDocumentEngine.s.sol +++ b/script/DeployDocumentEngine.s.sol @@ -14,6 +14,11 @@ import {DocumentEngine} from "../src/DocumentEngine.sol"; * Usage: * forge script script/DeployDocumentEngine.s.sol \ * --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast + * + * Warning: the environment variables above and passing a raw key with + * `--private-key` are for local testing only, not for production. For production + * use a secure signing method (encrypted keystore, hardware wallet, ...) as + * described in the Foundry Key Management documentation (getfoundry.sh). */ contract DeployDocumentEngine is Script { function run() external returns (DocumentEngine documentEngine) { diff --git a/script/DeployDocumentEngineOwnable.s.sol b/script/DeployDocumentEngineOwnable.s.sol index e76c813..5556474 100644 --- a/script/DeployDocumentEngineOwnable.s.sol +++ b/script/DeployDocumentEngineOwnable.s.sol @@ -14,6 +14,11 @@ import {DocumentEngineOwnable} from "../src/DocumentEngineOwnable.sol"; * Usage: * forge script script/DeployDocumentEngineOwnable.s.sol \ * --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast + * + * Warning: the environment variables above and passing a raw key with + * `--private-key` are for local testing only, not for production. For production + * use a secure signing method (encrypted keystore, hardware wallet, ...) as + * described in the Foundry Key Management documentation (getfoundry.sh). */ contract DeployDocumentEngineOwnable is Script { function run() external returns (DocumentEngineOwnable documentEngine) { From 34ec34daf06299c56aca9f2de179d28b7267c567 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 13:54:06 +0200 Subject: [PATCH 12/47] refactor: unify document-manager parameter naming on subject/subjects --- src/DocumentEngineBase.sol | 44 ++++++++++++++++----------------- src/DocumentEngineInvariant.sol | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/DocumentEngineBase.sol b/src/DocumentEngineBase.sol index b17cf8f..a0be988 100644 --- a/src/DocumentEngineBase.sol +++ b/src/DocumentEngineBase.sol @@ -123,21 +123,21 @@ abstract contract DocumentEngineBase is * @notice Batch version of setDocument to handle multiple documents at once */ function batchSetDocuments( - address[] calldata smartContracts, + address[] calldata subjects, bytes32[] calldata names, string[] calldata uris, bytes32[] calldata hashes ) external onlyDocumentManager { if ( - smartContracts.length == 0 || - smartContracts.length != names.length || + subjects.length == 0 || + subjects.length != names.length || names.length != uris.length || uris.length != hashes.length ) { revert InvalidInputLength(); } - for (uint256 i = 0; i < smartContracts.length; i++) { - _setDocument(smartContracts[i], names[i], uris[i], hashes[i]); + for (uint256 i = 0; i < subjects.length; i++) { + _setDocument(subjects[i], names[i], uris[i], hashes[i]); } } @@ -145,7 +145,7 @@ abstract contract DocumentEngineBase is * @notice Batch version of setDocument to handle multiple documents at once */ function batchSetDocuments( - address smartContract, + address subject, bytes32[] calldata names, string[] calldata uris, bytes32[] calldata hashes @@ -158,7 +158,7 @@ abstract contract DocumentEngineBase is revert InvalidInputLength(); } for (uint256 i = 0; i < names.length; ++i) { - _setDocument(smartContract, names[i], uris[i], hashes[i]); + _setDocument(subject, names[i], uris[i], hashes[i]); } } @@ -166,18 +166,18 @@ abstract contract DocumentEngineBase is * @notice Batch version of removeDocument to handle multiple documents at once */ function batchRemoveDocuments( - address[] calldata smartContracts, + address[] calldata subjects, bytes32[] calldata names ) external onlyDocumentManager { if ( - smartContracts.length == 0 || - (smartContracts.length != names.length) + subjects.length == 0 || + (subjects.length != names.length) ) { revert InvalidInputLength(); } - for (uint256 i = 0; i < smartContracts.length; ++i) { - _removeDocument(smartContracts[i], names[i]); + for (uint256 i = 0; i < subjects.length; ++i) { + _removeDocument(subjects[i], names[i]); } } @@ -185,7 +185,7 @@ abstract contract DocumentEngineBase is * @notice Batch version of removeDocument to handle multiple documents at once */ function batchRemoveDocuments( - address smartContract, + address subject, bytes32[] calldata names ) external onlyDocumentManager { if (names.length == 0) { @@ -193,7 +193,7 @@ abstract contract DocumentEngineBase is } for (uint256 i = 0; i < names.length; ++i) { - _removeDocument(smartContract, names[i]); + _removeDocument(subject, names[i]); } } @@ -245,26 +245,26 @@ abstract contract DocumentEngineBase is * @dev Internal function to fetch a document */ function _getDocument( - address smartContract, + address subject, bytes32 name_ ) internal view returns (Document memory) { - return _documents[smartContract][name_]; + return _documents[subject][name_]; } /** * @dev Internal helper to remove the document name from the list of document names */ function _removeDocumentName( - address smartContract, + address subject, bytes32 name_ ) internal { - uint256 length = _documentNames[smartContract].length; + uint256 length = _documentNames[subject].length; for (uint256 i = 0; i < length; ++i) { - if (_documentNames[smartContract][i] == name_) { - _documentNames[smartContract][i] = _documentNames[ - smartContract + if (_documentNames[subject][i] == name_) { + _documentNames[subject][i] = _documentNames[ + subject ][length - 1]; - _documentNames[smartContract].pop(); + _documentNames[subject].pop(); break; } } diff --git a/src/DocumentEngineInvariant.sol b/src/DocumentEngineInvariant.sol index 42e27a0..ffda586 100644 --- a/src/DocumentEngineInvariant.sol +++ b/src/DocumentEngineInvariant.sol @@ -10,7 +10,7 @@ pragma solidity ^0.8.20; * live in {DocumentEngine}, the owner logic in {DocumentEngineOwnable}). */ contract DocumentEngineInvariant { - error DocumentNotFound(address smartContract, bytes32 name); + error DocumentNotFound(address subject, bytes32 name); error InvalidInputLength(); error AdminWithAddressZeroNotAllowed(); From a8395629d070c39d618f7e73f60c4a87b978ebc0 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 14:02:16 +0200 Subject: [PATCH 13/47] feat: add shared ITokenBinding surface (bindToken/unbindToken/isTokenBound) to both deployments --- AGENTS.md | 18 +++++++++------ CHANGELOG.md | 4 ++++ CLAUDE.md | 18 +++++++++------ README.md | 26 ++++++++++++++++------ src/DocumentEngine.sol | 38 +++++++++++++++++++++++++++++++- src/DocumentEngineOwnable.sol | 33 +++++++++++++++++---------- src/interfaces/ITokenBinding.sol | 26 ++++++++++++++++++++++ test/DocumentEngine.t.sol | 34 +++++++++++++++++++++++++--- test/DocumentEngineOwnable.t.sol | 36 ++++++++++++++++++++++++++---- 9 files changed, 192 insertions(+), 41 deletions(-) create mode 100644 src/interfaces/ITokenBinding.sol diff --git a/AGENTS.md b/AGENTS.md index 3176959..065f68c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,10 +23,12 @@ addressed by a `bytes32` name. - **Admin path** — `DOCUMENT_MANAGER_ROLE`. Address-scoped overloads (`setDocument(address,...)`, `removeDocument(address,...)`, batch variants) manage documents for any contract. - - **Bound-token path** — `TOKEN_CONTRACT_ROLE` (the CMTA RuleEngine binding - pattern). The standard single-arg `IERC1643` functions (`setDocument(name,uri,hash)`, - `removeDocument(name)`) let a bound token manage its **own** namespace - (`_msgSender()`). Bind a token with `grantRole(TOKEN_CONTRACT_ROLE, token)`. + - **Bound-token path** — the standard single-arg `IERC1643` functions + (`setDocument(name,uri,hash)`, `removeDocument(name)`) let a bound token manage + its **own** namespace (`_msgSender()`). Bind via the shared `ITokenBinding` + surface: `bindToken(token)` / `unbindToken(token)` / `isTokenBound(token)` + (uniform across both deployments). Role deployment binds over + `TOKEN_CONTRACT_ROLE` (CMTA RuleEngine pattern); Ownable over an owner allowlist. NOTE: RuleEngine's `ERC3643ComplianceExtendedModule` is intentionally **not** reused for binding — it is an `IERC3643Compliance`, which would drag in transfer-compliance callbacks (`canTransfer`/`transferred`/`created`/`destroyed`) @@ -67,13 +69,15 @@ src/ │ # DOCUMENT_MANAGER_ROLE / TOKEN_CONTRACT_ROLE, _authorize* │ # impls, hasRole), ERC-2771, supportsInterface, constructor ├── DocumentEngineOwnable.sol # Deployment #2: Ownable2Step (single owner) instead of -│ # roles; owner-managed token binding (setTokenBinding) +│ # roles; owner-managed token binding (ITokenBinding) ├── DocumentEngineInvariant.sol # Shared errors only (incl. ERC1643InvalidName / │ # ERC1643MissingDocument); NO access-control specifics ├── interfaces/ │ ├── IERC8303.sol # ERC-8303 "Contract Version" interface (id 0x54fd4d50) -│ └── IERC1643MultiDocument.sol # Multi-token ERC-1643 extension (address-scoped fns + -│ # DocumentUpdatedForSubject / DocumentRemovedForSubject) +│ ├── IERC1643MultiDocument.sol # Multi-token ERC-1643 extension (address-scoped fns + +│ │ # DocumentUpdatedForSubject / DocumentRemovedForSubject) +│ └── ITokenBinding.sol # Shared binding surface: bindToken / unbindToken / +│ # isTokenBound + TokenBindingSet (both deployments) └── modules/ └── VersionModule.sol # Version module: implements ERC-8303 version() + ERC-165, # holds the VERSION constant (currently "0.4.0") diff --git a/CHANGELOG.md b/CHANGELOG.md index 452f7f9..bb75132 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,6 +78,10 @@ Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/er - **Input validation.** `setDocument` now reverts `ERC1643InvalidName()` when `name == bytes32(0)`; `removeDocument` now reverts `ERC1643MissingDocument()` for a non-existent document (previously it silently emitted a spurious removal event). - **ERC-165 discovery.** `supportsInterface` now returns `true` for `type(IERC1643).interfaceId` and `type(IERC1643MultiDocument).interfaceId` (both deployments). +### Added (token binding) + +- **Shared `ITokenBinding` interface** (`src/interfaces/ITokenBinding.sol`): `bindToken(token)` / `unbindToken(token)` / `isTokenBound(token)` + `TokenBindingSet` event. Both deployments now implement it (and advertise `type(ITokenBinding).interfaceId` via ERC-165), so integrators bind/query a token the same way regardless of the access-control model. `DocumentEngine` implements it over `TOKEN_CONTRACT_ROLE` (grant/revoke/hasRole); `DocumentEngineOwnable` over its owner-managed allowlist (replacing the previous `setTokenBinding` / `isBoundToken`). The revert on an unbound write still differs per deployment (`AccessControlUnauthorizedAccount` vs `NotBoundToken`). + ### Notes / bottlenecks - CMTAT v3 no longer ships a *standalone* token that consumes an external document engine through its constructor; the standard token stores documents on-chain (`DocumentERC1643Module`). External-engine integration now goes through CMTAT's `DocumentEngineModule` (`setDocumentEngine`). The test suite was updated to exercise this real integration path via a minimal token built on `DocumentEngineModule`. diff --git a/CLAUDE.md b/CLAUDE.md index 3176959..065f68c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -23,10 +23,12 @@ addressed by a `bytes32` name. - **Admin path** — `DOCUMENT_MANAGER_ROLE`. Address-scoped overloads (`setDocument(address,...)`, `removeDocument(address,...)`, batch variants) manage documents for any contract. - - **Bound-token path** — `TOKEN_CONTRACT_ROLE` (the CMTA RuleEngine binding - pattern). The standard single-arg `IERC1643` functions (`setDocument(name,uri,hash)`, - `removeDocument(name)`) let a bound token manage its **own** namespace - (`_msgSender()`). Bind a token with `grantRole(TOKEN_CONTRACT_ROLE, token)`. + - **Bound-token path** — the standard single-arg `IERC1643` functions + (`setDocument(name,uri,hash)`, `removeDocument(name)`) let a bound token manage + its **own** namespace (`_msgSender()`). Bind via the shared `ITokenBinding` + surface: `bindToken(token)` / `unbindToken(token)` / `isTokenBound(token)` + (uniform across both deployments). Role deployment binds over + `TOKEN_CONTRACT_ROLE` (CMTA RuleEngine pattern); Ownable over an owner allowlist. NOTE: RuleEngine's `ERC3643ComplianceExtendedModule` is intentionally **not** reused for binding — it is an `IERC3643Compliance`, which would drag in transfer-compliance callbacks (`canTransfer`/`transferred`/`created`/`destroyed`) @@ -67,13 +69,15 @@ src/ │ # DOCUMENT_MANAGER_ROLE / TOKEN_CONTRACT_ROLE, _authorize* │ # impls, hasRole), ERC-2771, supportsInterface, constructor ├── DocumentEngineOwnable.sol # Deployment #2: Ownable2Step (single owner) instead of -│ # roles; owner-managed token binding (setTokenBinding) +│ # roles; owner-managed token binding (ITokenBinding) ├── DocumentEngineInvariant.sol # Shared errors only (incl. ERC1643InvalidName / │ # ERC1643MissingDocument); NO access-control specifics ├── interfaces/ │ ├── IERC8303.sol # ERC-8303 "Contract Version" interface (id 0x54fd4d50) -│ └── IERC1643MultiDocument.sol # Multi-token ERC-1643 extension (address-scoped fns + -│ # DocumentUpdatedForSubject / DocumentRemovedForSubject) +│ ├── IERC1643MultiDocument.sol # Multi-token ERC-1643 extension (address-scoped fns + +│ │ # DocumentUpdatedForSubject / DocumentRemovedForSubject) +│ └── ITokenBinding.sol # Shared binding surface: bindToken / unbindToken / +│ # isTokenBound + TokenBindingSet (both deployments) └── modules/ └── VersionModule.sol # Version module: implements ERC-8303 version() + ERC-165, # holds the VERSION constant (currently "0.4.0") diff --git a/README.md b/README.md index ddba5bd..134a6b2 100644 --- a/README.md +++ b/README.md @@ -49,15 +49,23 @@ function setDocument(address smartContract, bytes32 name_, string memory uri_, b function removeDocument(address smartContract, bytes32 name_) external; ``` -**2. Bound-token path (`TOKEN_CONTRACT_ROLE`).** This implements the standard, -single-argument ERC-1643 functions. A token is *bound* to the engine by being -granted `TOKEN_CONTRACT_ROLE` (the same binding pattern as the CMTA -[RuleEngine](https://github.com/CMTA/RuleEngine)): +**2. Bound-token path.** This implements the standard, single-argument ERC-1643 +functions. A token is *bound* to the engine through the shared **`ITokenBinding`** +surface — identical across both deployments, so integrators bind/query a token the +same way regardless of the access-control model: ```solidity -documentEngine.grantRole(TOKEN_CONTRACT_ROLE, address(token)); +documentEngine.bindToken(address(token)); // also: unbindToken(token), isTokenBound(token) ``` +Under the hood the role-based `DocumentEngine` binds by granting +`TOKEN_CONTRACT_ROLE` (the CMTA [RuleEngine](https://github.com/CMTA/RuleEngine) +pattern) and the `DocumentEngineOwnable` uses an owner-managed allowlist; both +expose the same `bindToken` / `unbindToken` / `isTokenBound` functions and the +`TokenBindingSet` event. (The revert raised when a *non-bound* caller attempts a +write differs — `AccessControlUnauthorizedAccount` vs `NotBoundToken` — since it +comes from each deployment's access-control model.) + Once bound, the token manages its **own** documents (`msg.sender` is the token); it can never affect another contract's documents: @@ -164,8 +172,12 @@ The engine is split into two contracts (CMTAT module/deployment pattern): the members of each role on-chain. - **`DocumentEngineOwnable`** (alternative deployment) — same base logic, but access control is a single **owner** via `Ownable2Step` (two-step ownership - transfer) instead of roles. Admin management is `owner`-only; the bound-token - path uses an owner-managed allowlist (`setTokenBinding` / `isBoundToken`). + transfer) instead of roles. Admin management is `owner`-only; token binding uses + an owner-managed allowlist behind the shared `ITokenBinding` surface. + +Both deployments implement the shared **`ITokenBinding`** interface +(`bindToken` / `unbindToken` / `isTokenBound` + `TokenBindingSet`), so the binding +surface is uniform and ERC-165-discoverable regardless of the access-control model. `DocumentEngineInvariant` provides the errors and the optional multi-token events shared by every deployment. Access-control specifics are **not** defined there: diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index b837127..4e94b22 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -5,6 +5,7 @@ import "OZ/access/extensions/AccessControlEnumerable.sol"; import {IAccessControl} from "OZ/access/IAccessControl.sol"; import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; import {IERC1643MultiDocument} from "./interfaces/IERC1643MultiDocument.sol"; +import {ITokenBinding} from "./interfaces/ITokenBinding.sol"; import "OZ/metatx/ERC2771Context.sol"; import "./DocumentEngineBase.sol"; import "./modules/VersionModule.sol"; @@ -24,7 +25,8 @@ contract DocumentEngine is DocumentEngineBase, VersionModule, AccessControlEnumerable, - ERC2771Context + ERC2771Context, + ITokenBinding { // Role allowed to manage documents on behalf of any smart contract (admin path) bytes32 public constant DOCUMENT_MANAGER_ROLE = @@ -73,6 +75,39 @@ contract DocumentEngine is _checkRole(TOKEN_CONTRACT_ROLE); } + /* ============ Token binding (ITokenBinding) ============ */ + + /** + * @inheritdoc ITokenBinding + * @dev Binding a token is granting it `TOKEN_CONTRACT_ROLE`. Authorization is + * that of {AccessControl-grantRole} (the role admin of `TOKEN_CONTRACT_ROLE`, + * i.e. `DEFAULT_ADMIN_ROLE` by default). + */ + function bindToken(address token) external override { + grantRole(TOKEN_CONTRACT_ROLE, token); + emit TokenBindingSet(token, true); + } + + /** + * @inheritdoc ITokenBinding + * @dev Unbinding a token is revoking its `TOKEN_CONTRACT_ROLE`. + */ + function unbindToken(address token) external override { + revokeRole(TOKEN_CONTRACT_ROLE, token); + emit TokenBindingSet(token, false); + } + + /** + * @inheritdoc ITokenBinding + * @dev Note: because the default admin holds every role (see {hasRole}), this + * returns `true` for a `DEFAULT_ADMIN_ROLE` holder as well. + */ + function isTokenBound( + address token + ) external view override returns (bool) { + return hasRole(TOKEN_CONTRACT_ROLE, token); + } + /* * @dev Returns `true` if `account` has been granted `role`. */ @@ -107,6 +142,7 @@ contract DocumentEngine is return interfaceId == type(IERC1643).interfaceId || interfaceId == type(IERC1643MultiDocument).interfaceId || + interfaceId == type(ITokenBinding).interfaceId || super.supportsInterface(interfaceId); } diff --git a/src/DocumentEngineOwnable.sol b/src/DocumentEngineOwnable.sol index b221e9d..c70821d 100644 --- a/src/DocumentEngineOwnable.sol +++ b/src/DocumentEngineOwnable.sol @@ -5,6 +5,7 @@ import {Ownable} from "OZ/access/Ownable.sol"; import {Ownable2Step} from "OZ/access/Ownable2Step.sol"; import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; import {IERC1643MultiDocument} from "./interfaces/IERC1643MultiDocument.sol"; +import {ITokenBinding} from "./interfaces/ITokenBinding.sol"; import "OZ/metatx/ERC2771Context.sol"; import "./DocumentEngineBase.sol"; import "./modules/VersionModule.sol"; @@ -27,14 +28,12 @@ contract DocumentEngineOwnable is DocumentEngineBase, VersionModule, Ownable2Step, - ERC2771Context + ERC2771Context, + ITokenBinding { /// @dev Tokens bound to the engine, allowed to manage their own documents. mapping(address => bool) private _boundTokens; - /// @notice Emitted when a token binding is set or removed by the owner. - event TokenBindingSet(address indexed token, bool bound); - /// @notice Thrown when a non-bound caller uses the bound-token path. error NotBoundToken(address caller); @@ -48,20 +47,29 @@ contract DocumentEngineOwnable is ) Ownable(owner_) ERC2771Context(forwarderIrrevocable) {} /*////////////////////////////////////////////////////////////// - TOKEN BINDING + TOKEN BINDING (ITokenBinding) //////////////////////////////////////////////////////////////*/ /** - * @notice Bind or unbind a token, allowing it to manage its own documents. - * @dev Owner-managed analog of granting/revoking `TOKEN_CONTRACT_ROLE`. + * @inheritdoc ITokenBinding + * @dev Owner-managed analog of granting `TOKEN_CONTRACT_ROLE`. + */ + function bindToken(address token) external override onlyOwner { + _boundTokens[token] = true; + emit TokenBindingSet(token, true); + } + + /** + * @inheritdoc ITokenBinding + * @dev Owner-managed analog of revoking `TOKEN_CONTRACT_ROLE`. */ - function setTokenBinding(address token, bool bound) external onlyOwner { - _boundTokens[token] = bound; - emit TokenBindingSet(token, bound); + function unbindToken(address token) external override onlyOwner { + _boundTokens[token] = false; + emit TokenBindingSet(token, false); } - /// @notice Returns whether `token` is bound to the engine. - function isBoundToken(address token) external view returns (bool) { + /// @inheritdoc ITokenBinding + function isTokenBound(address token) external view override returns (bool) { return _boundTokens[token]; } @@ -101,6 +109,7 @@ contract DocumentEngineOwnable is return interfaceId == type(IERC1643).interfaceId || interfaceId == type(IERC1643MultiDocument).interfaceId || + interfaceId == type(ITokenBinding).interfaceId || super.supportsInterface(interfaceId); } diff --git a/src/interfaces/ITokenBinding.sol b/src/interfaces/ITokenBinding.sol new file mode 100644 index 0000000..e9ec45c --- /dev/null +++ b/src/interfaces/ITokenBinding.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.20; + +/** + * @title ITokenBinding + * @notice Common token-binding surface shared by every DocumentEngine deployment, + * so integrators bind, unbind and query a token the same way regardless of the + * underlying access-control model (role-based or owner-based). + * @dev A *bound* token is allowed to manage its own documents through the standard + * single-argument ERC-1643 functions (`msg.sender` is the token). Binding is a + * privileged operation; the exact authorization (a role, the owner, ...) and the + * revert raised when a non-bound caller attempts a write are deployment-specific. + */ +interface ITokenBinding { + /// @notice Emitted when a token is bound (`bound = true`) or unbound (`bound = false`). + event TokenBindingSet(address indexed token, bool bound); + + /// @notice Binds `token`, allowing it to manage its own documents. + function bindToken(address token) external; + + /// @notice Unbinds `token`. + function unbindToken(address token) external; + + /// @notice Returns whether `token` is currently bound. + function isTokenBound(address token) external view returns (bool); +} diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index 77de7f3..7a5a2fc 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -8,6 +8,7 @@ import "OZ/access/AccessControl.sol"; import {IERC165} from "OZ/utils/introspection/IERC165.sol"; import {IERC8303} from "../src/interfaces/IERC8303.sol"; import {IERC1643MultiDocument} from "../src/interfaces/IERC1643MultiDocument.sol"; +import {ITokenBinding} from "../src/interfaces/ITokenBinding.sol"; import {DocumentEngineModule} from "CMTAT/modules/wrapper/options/DocumentEngineModule.sol"; /** @@ -232,9 +233,10 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { //////////////////////////////////////////////////////////////*/ function testBoundTokenCanManageOwnDocument() public { - // Bind the token to the engine + // Bind the token to the engine (shared ITokenBinding surface) vm.prank(admin); - documentEngine.grantRole(TOKEN_CONTRACT_ROLE, testContract); + documentEngine.bindToken(testContract); + assertTrue(documentEngine.isTokenBound(testContract)); // The bound token manages its own document namespace (msg.sender) bytes32 selfName = keccak256("self-doc"); @@ -261,6 +263,28 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { assertEq(doc.lastModified, 0); } + function testNonAdminCannotBindToken() public { + vm.prank(attacker); + vm.expectRevert( + abi.encodeWithSelector( + AccessControlUnauthorizedAccount.selector, + attacker, + DEFAULT_ADMIN_ROLE + ) + ); + documentEngine.bindToken(testContract); + } + + function testAdminCanUnbindToken() public { + vm.prank(admin); + documentEngine.bindToken(testContract); + assertTrue(documentEngine.isTokenBound(testContract)); + + vm.prank(admin); + documentEngine.unbindToken(testContract); + assertFalse(documentEngine.isTokenBound(testContract)); + } + function testUnboundContractCannotSetOwnDocument() public { bytes32 selfName = keccak256("self-doc"); vm.prank(attacker); @@ -344,6 +368,10 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { type(IERC1643MultiDocument).interfaceId ) ); + // ...and the shared token-binding surface + assertTrue( + documentEngine.supportsInterface(type(ITokenBinding).interfaceId) + ); } /*////////////////////////////////////////////////////////////// @@ -373,7 +401,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { function testBoundTokenCannotSetZeroName() public { vm.prank(admin); - documentEngine.grantRole(TOKEN_CONTRACT_ROLE, testContract); + documentEngine.bindToken(testContract); vm.prank(testContract); vm.expectRevert( abi.encodeWithSelector(ERC1643InvalidName.selector) diff --git a/test/DocumentEngineOwnable.t.sol b/test/DocumentEngineOwnable.t.sol index 3117c94..5d8ae80 100644 --- a/test/DocumentEngineOwnable.t.sol +++ b/test/DocumentEngineOwnable.t.sol @@ -7,6 +7,9 @@ import {Ownable} from "OZ/access/Ownable.sol"; import {IAccessControl} from "OZ/access/IAccessControl.sol"; import {IERC165} from "OZ/utils/introspection/IERC165.sol"; import {IERC8303} from "../src/interfaces/IERC8303.sol"; +import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; +import {IERC1643MultiDocument} from "../src/interfaces/IERC1643MultiDocument.sol"; +import {ITokenBinding} from "../src/interfaces/ITokenBinding.sol"; contract DocumentEngineOwnableTest is Test { DocumentEngineOwnable public engine; @@ -71,8 +74,8 @@ contract DocumentEngineOwnableTest is Test { function testOwnerCanBindToken() public { vm.prank(owner); - engine.setTokenBinding(testContract, true); - assertTrue(engine.isBoundToken(testContract)); + engine.bindToken(testContract); + assertTrue(engine.isTokenBound(testContract)); } function testNonOwnerCannotBindToken() public { @@ -83,12 +86,12 @@ contract DocumentEngineOwnableTest is Test { attacker ) ); - engine.setTokenBinding(testContract, true); + engine.bindToken(testContract); } function testBoundTokenCanManageOwnDocument() public { vm.prank(owner); - engine.setTokenBinding(testContract, true); + engine.bindToken(testContract); vm.prank(testContract); engine.setDocument(documentName, documentURI, documentHash); @@ -105,6 +108,26 @@ contract DocumentEngineOwnableTest is Test { assertEq(doc.lastModified, 0); } + function testUnbindTokenRevokesSelfManagement() public { + vm.prank(owner); + engine.bindToken(testContract); + assertTrue(engine.isTokenBound(testContract)); + + vm.prank(owner); + engine.unbindToken(testContract); + assertFalse(engine.isTokenBound(testContract)); + + // once unbound, the token can no longer self-manage + vm.prank(testContract); + vm.expectRevert( + abi.encodeWithSelector( + DocumentEngineOwnable.NotBoundToken.selector, + testContract + ) + ); + engine.setDocument(documentName, documentURI, documentHash); + } + function testUnboundTokenCannotSelfManage() public { vm.prank(attacker); vm.expectRevert( @@ -137,6 +160,11 @@ contract DocumentEngineOwnableTest is Test { assertEq(engine.version(), "0.4.0"); assertTrue(engine.supportsInterface(type(IERC8303).interfaceId)); assertTrue(engine.supportsInterface(type(IERC165).interfaceId)); + assertTrue(engine.supportsInterface(type(IERC1643).interfaceId)); + assertTrue( + engine.supportsInterface(type(IERC1643MultiDocument).interfaceId) + ); + assertTrue(engine.supportsInterface(type(ITokenBinding).interfaceId)); // no role-based access control here assertFalse(engine.supportsInterface(type(IAccessControl).interfaceId)); assertFalse(engine.supportsInterface(bytes4(0xffffffff))); From a77a9226089053593a291a65fd5f935a654ba32c Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 14:04:55 +0200 Subject: [PATCH 14/47] docs: clarify base/invariant/hasRole NatSpec and mark DocumentEngineInvariant abstract --- src/DocumentEngine.sol | 10 ++++++++-- src/DocumentEngineBase.sol | 7 ++++--- src/DocumentEngineInvariant.sol | 9 +++++---- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index 4e94b22..4dc9701 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -108,8 +108,14 @@ contract DocumentEngine is return hasRole(TOKEN_CONTRACT_ROLE, token); } - /* - * @dev Returns `true` if `account` has been granted `role`. + /** + * @dev Returns `true` if `account` has been granted `role`. The default admin + * (`DEFAULT_ADMIN_ROLE`) is treated as holding **every** role. + * + * Note: this virtual "admin has all roles" behavior is NOT reflected by + * {AccessControlEnumerable} enumeration. `getRoleMember` / `getRoleMemberCount` + * report only explicit grants, so a `DEFAULT_ADMIN_ROLE` holder satisfies + * `hasRole(anyRole, admin)` yet does not appear in `getRoleMember(anyRole, ...)`. */ function hasRole( bytes32 role, diff --git a/src/DocumentEngineBase.sol b/src/DocumentEngineBase.sol index a0be988..4796066 100644 --- a/src/DocumentEngineBase.sol +++ b/src/DocumentEngineBase.sol @@ -97,9 +97,10 @@ abstract contract DocumentEngineBase is /** * @notice ERC-1643 function to set or update a document for the caller. * @dev The document is stored under the caller (`_msgSender()`) namespace. - * The caller must be a token bound to this engine (`TOKEN_CONTRACT_ROLE`), - * following the RuleEngine binding pattern. A bound token can only manage - * its own documents; it can never affect another contract's documents. + * Restricted by the `onlyBoundToken` hook: the caller must be a token bound to + * this engine. How a token is bound is deployment-specific (see the + * {_authorizeBoundTokenDocumentManagement} implementations). A bound token can + * only manage its own documents; it can never affect another contract's documents. */ function setDocument( bytes32 name_, diff --git a/src/DocumentEngineInvariant.sol b/src/DocumentEngineInvariant.sol index ffda586..93bfb37 100644 --- a/src/DocumentEngineInvariant.sol +++ b/src/DocumentEngineInvariant.sol @@ -3,13 +3,14 @@ pragma solidity ^0.8.20; /** * @title DocumentEngineInvariant - * @notice Shared errors and events for the DocumentEngine, common to every - * deployment regardless of its access-control model. + * @notice Shared errors for the DocumentEngine, common to every deployment + * regardless of its access-control model. * @dev Access-control specifics (roles, owner, ...) are intentionally NOT * defined here; they belong to the deployment contract (e.g. the role constants - * live in {DocumentEngine}, the owner logic in {DocumentEngineOwnable}). + * live in {DocumentEngine}, the owner logic in {DocumentEngineOwnable}). This + * contract is only ever used as a base, never deployed on its own. */ -contract DocumentEngineInvariant { +abstract contract DocumentEngineInvariant { error DocumentNotFound(address subject, bytes32 name); error InvalidInputLength(); error AdminWithAddressZeroNotAllowed(); From f1ae06d041b4cdbdc1a2ea5bdc838b927e8ef3db Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 18:23:57 +0200 Subject: [PATCH 15/47] refactor: unify token binding into shared TokenBindingModule (allowlist), drop TOKEN_CONTRACT_ROLE --- AGENTS.md | 30 ++++++------ CHANGELOG.md | 10 ++-- CLAUDE.md | 30 ++++++------ README.md | 61 ++++++++++++++---------- src/DocumentEngine.sol | 64 +++---------------------- src/DocumentEngineOwnable.sol | 73 +++++------------------------ src/modules/TokenBindingModule.sol | 75 ++++++++++++++++++++++++++++++ test/DocumentEngine.t.sol | 17 +++---- test/DocumentEngineOwnable.t.sol | 5 +- 9 files changed, 177 insertions(+), 188 deletions(-) create mode 100644 src/modules/TokenBindingModule.sol diff --git a/AGENTS.md b/AGENTS.md index 065f68c..6a86b3d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,9 +26,10 @@ addressed by a `bytes32` name. - **Bound-token path** — the standard single-arg `IERC1643` functions (`setDocument(name,uri,hash)`, `removeDocument(name)`) let a bound token manage its **own** namespace (`_msgSender()`). Bind via the shared `ITokenBinding` - surface: `bindToken(token)` / `unbindToken(token)` / `isTokenBound(token)` - (uniform across both deployments). Role deployment binds over - `TOKEN_CONTRACT_ROLE` (CMTA RuleEngine pattern); Ownable over an owner allowlist. + surface: `bindToken(token)` / `unbindToken(token)` / `isTokenBound(token)`, + implemented **once** for both deployments by `TokenBindingModule` — a single + allowlist, NOT a role (there is no `TOKEN_CONTRACT_ROLE`). Binding is authorized + by each deployment's document-management hook (DOCUMENT_MANAGER_ROLE / owner). NOTE: RuleEngine's `ERC3643ComplianceExtendedModule` is intentionally **not** reused for binding — it is an `IERC3643Compliance`, which would drag in transfer-compliance callbacks (`canTransfer`/`transferred`/`created`/`destroyed`) @@ -47,10 +48,11 @@ addressed by a `bytes32` name. `hasRole` override). - **Flexible access control (CMTAT / RuleEngine pattern):** restricted functions use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to - overridable `internal virtual` hooks `_authorizeDocumentManagement()` / - `_authorizeBoundTokenDocumentManagement()` (default `DOCUMENT_MANAGER_ROLE` / - `TOKEN_CONTRACT_ROLE`). Keep the management implementation separate from the - authorization logic — change *who* is authorized by overriding a hook, not by + overridable `internal virtual` hooks `_authorizeDocumentManagement()` (per + deployment: `DOCUMENT_MANAGER_ROLE` / owner) and + `_authorizeBoundTokenDocumentManagement()` (implemented once by + `TokenBindingModule` → allowlist check). Keep the management implementation + separate from the authorization logic — change *who* is authorized via a hook, not by editing the management functions. - **CMTAT integration:** since CMTAT v3, a token uses the engine via CMTAT's `DocumentEngineModule` and `setDocumentEngine(engine)` (reads/writes are forwarded @@ -65,11 +67,11 @@ src/ │ # both management paths, batch functions, modifiers, │ # and the ABSTRACT _authorize* hooks (no access control) ├── DocumentEngine.sol # Deployment #1: role-based access control -│ # (AccessControlEnumerable, the role constants -│ # DOCUMENT_MANAGER_ROLE / TOKEN_CONTRACT_ROLE, _authorize* -│ # impls, hasRole), ERC-2771, supportsInterface, constructor +│ # (AccessControlEnumerable, DOCUMENT_MANAGER_ROLE, +│ # _authorizeDocumentManagement, hasRole), ERC-2771, +│ # supportsInterface, constructor ├── DocumentEngineOwnable.sol # Deployment #2: Ownable2Step (single owner) instead of -│ # roles; owner-managed token binding (ITokenBinding) +│ # roles; document mgmt + binding are owner-only ├── DocumentEngineInvariant.sol # Shared errors only (incl. ERC1643InvalidName / │ # ERC1643MissingDocument); NO access-control specifics ├── interfaces/ @@ -79,8 +81,10 @@ src/ │ └── ITokenBinding.sol # Shared binding surface: bindToken / unbindToken / │ # isTokenBound + TokenBindingSet (both deployments) └── modules/ - └── VersionModule.sol # Version module: implements ERC-8303 version() + ERC-165, - # holds the VERSION constant (currently "0.4.0") + ├── VersionModule.sol # Version module: implements ERC-8303 version() + ERC-165, + │ # holds the VERSION constant (currently "0.4.0") + └── TokenBindingModule.sol # Shared token-binding allowlist (ITokenBinding) + NotBoundToken; + # wires the bound-token hook; used by both deployments script/ ├── DeployDocumentEngine.s.sol # Deploy role-based DocumentEngine (env: DOCUMENT_ENGINE_ADMIN, diff --git a/CHANGELOG.md b/CHANGELOG.md index bb75132..e9205be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,17 +57,17 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) ### Added -- **Bound-token document management (RuleEngine binding pattern)**: implement the now-mandatory `IERC1643.setDocument(name, uri, hash)` and `removeDocument(name)`. They are gated by a new `TOKEN_CONTRACT_ROLE` and scoped to the caller (`_msgSender()`) own namespace. A token bound with `grantRole(TOKEN_CONTRACT_ROLE, token)` manages its own documents and can never affect another contract's documents. The existing admin overloads (explicit `address`, `DOCUMENT_MANAGER_ROLE`) are unchanged, so both systems work side by side. (RuleEngine's `ERC3643ComplianceExtendedModule` was evaluated for this but intentionally not reused — see the README.) +- **Bound-token document management**: implement the now-mandatory `IERC1643.setDocument(name, uri, hash)` and `removeDocument(name)`, gated by the `onlyBoundToken` modifier and scoped to the caller (`_msgSender()`) own namespace. A token bound with `bindToken(token)` (see the shared binding module below) manages its own documents and can never affect another contract's documents. The admin overloads (explicit `address`, `DOCUMENT_MANAGER_ROLE`) are unchanged, so both systems work side by side. (RuleEngine's `ERC3643ComplianceExtendedModule` was evaluated for the binding but intentionally not reused — see the README.) - **Optional multi-token events**: alongside the standard `IERC1643` events, the engine now also emits `DocumentUpdatedForContract` / `DocumentRemovedForContract`, which carry the `smartContract` (token) address so off-chain indexers can tell which contract a document belongs to during multi-contract operations. See [`ERC-1643-proposition.md`](./doc/ERCSpecification/ERC-1643-proposition.md) for the proposed optional standard extension. -- **Flexible access control (CMTAT / RuleEngine pattern)**: the restricted functions now use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to overridable `internal virtual` authorization hooks `_authorizeDocumentManagement()` / `_authorizeBoundTokenDocumentManagement()` (default: `DOCUMENT_MANAGER_ROLE` / `TOKEN_CONTRACT_ROLE`). This separates the document-management implementation from the authorization logic, so a subclass can change *who* is authorized without touching the management functions. Default behavior is unchanged. +- **Flexible access control (CMTAT / RuleEngine pattern)**: the restricted functions use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to overridable `internal virtual` authorization hooks `_authorizeDocumentManagement()` / `_authorizeBoundTokenDocumentManagement()`. Each deployment implements the admin hook (`DOCUMENT_MANAGER_ROLE` or `owner`); the bound-token hook is implemented once by `TokenBindingModule` (the shared allowlist). This separates the document-management implementation from the authorization logic. - **Split into a base contract and a deployment contract** (CMTAT module/deployment pattern): the document-management logic and storage now live in the new abstract `DocumentEngineBase` (with abstract `_authorize*` hooks), while `DocumentEngine` is the deployment contract that defines the access control (`AccessControl`, the concrete hooks and `hasRole`) and the ERC-2771 wiring. The deployable `DocumentEngine` API and behavior are unchanged. - **Version module implementing ERC-8303**: the version is now exposed through a dedicated `VersionModule` (`src/modules/VersionModule.sol`) implementing the `IERC8303` interface (`src/interfaces/IERC8303.sol`). It adds a standard `version()` view function (in addition to the existing public `VERSION` constant) and advertises ERC-8303 via ERC-165 (`supportsInterface(0x54fd4d50) == true`). `DocumentEngine` combines the module's `supportsInterface` with the access-control base. -- **Second deployment `DocumentEngineOwnable`** (`src/DocumentEngineOwnable.sol`): an alternative deployment that uses OpenZeppelin `Ownable2Step` (single owner, two-step transfer) instead of role-based access control, reusing the same `DocumentEngineBase` logic. Admin document management is restricted to the `owner`; the bound-token path uses an owner-managed binding allowlist (`setTokenBinding` / `isBoundToken`, the analog of `TOKEN_CONTRACT_ROLE`, reverting with `NotBoundToken`). +- **Second deployment `DocumentEngineOwnable`** (`src/DocumentEngineOwnable.sol`): an alternative deployment that uses OpenZeppelin `Ownable2Step` (single owner, two-step transfer) instead of role-based access control, reusing the same `DocumentEngineBase` logic and the shared `TokenBindingModule`. Both document management and token binding are restricted to the `owner`. ### Changed (access control) - `DocumentEngine` now inherits **`AccessControlEnumerable`** instead of `AccessControl`, adding on-chain enumeration of role members (`getRoleMember`, `getRoleMemberCount`) and advertising `IAccessControlEnumerable` via ERC-165. Default authorization behavior is unchanged. -- Moved the role constants (`DOCUMENT_MANAGER_ROLE`, `TOKEN_CONTRACT_ROLE`) out of the shared `DocumentEngineInvariant` and into the role-based `DocumentEngine`, so `DocumentEngineInvariant` (and the `DocumentEngineOwnable` deployment) no longer carry access-control-specific constants. The invariant now holds only the shared errors and multi-token events. +- Moved the `DOCUMENT_MANAGER_ROLE` constant out of the shared `DocumentEngineInvariant` and into the role-based `DocumentEngine`, so `DocumentEngineInvariant` (and the `DocumentEngineOwnable` deployment) no longer carry access-control-specific constants. The invariant now holds only the shared errors. ### Fixed (ERC-1643 conformance) @@ -80,7 +80,7 @@ Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/er ### Added (token binding) -- **Shared `ITokenBinding` interface** (`src/interfaces/ITokenBinding.sol`): `bindToken(token)` / `unbindToken(token)` / `isTokenBound(token)` + `TokenBindingSet` event. Both deployments now implement it (and advertise `type(ITokenBinding).interfaceId` via ERC-165), so integrators bind/query a token the same way regardless of the access-control model. `DocumentEngine` implements it over `TOKEN_CONTRACT_ROLE` (grant/revoke/hasRole); `DocumentEngineOwnable` over its owner-managed allowlist (replacing the previous `setTokenBinding` / `isBoundToken`). The revert on an unbound write still differs per deployment (`AccessControlUnauthorizedAccount` vs `NotBoundToken`). +- **Shared `ITokenBinding` interface + `TokenBindingModule`.** `bindToken(token)` / `unbindToken(token)` / `isTokenBound(token)` + `TokenBindingSet` event (`src/interfaces/ITokenBinding.sol`), implemented once for both deployments by `src/modules/TokenBindingModule.sol` — a single **allowlist**, not a role. Both deployments now share the exact same binding mechanism (same functions, event, and `NotBoundToken` revert on an unbound write) and advertise `type(ITokenBinding).interfaceId` via ERC-165. The role deployment **no longer uses `TOKEN_CONTRACT_ROLE`** (removed) — binding is authorized by the document-management hook (`DOCUMENT_MANAGER_ROLE`, or the `owner` in `DocumentEngineOwnable`). ### Notes / bottlenecks diff --git a/CLAUDE.md b/CLAUDE.md index 065f68c..6a86b3d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,9 +26,10 @@ addressed by a `bytes32` name. - **Bound-token path** — the standard single-arg `IERC1643` functions (`setDocument(name,uri,hash)`, `removeDocument(name)`) let a bound token manage its **own** namespace (`_msgSender()`). Bind via the shared `ITokenBinding` - surface: `bindToken(token)` / `unbindToken(token)` / `isTokenBound(token)` - (uniform across both deployments). Role deployment binds over - `TOKEN_CONTRACT_ROLE` (CMTA RuleEngine pattern); Ownable over an owner allowlist. + surface: `bindToken(token)` / `unbindToken(token)` / `isTokenBound(token)`, + implemented **once** for both deployments by `TokenBindingModule` — a single + allowlist, NOT a role (there is no `TOKEN_CONTRACT_ROLE`). Binding is authorized + by each deployment's document-management hook (DOCUMENT_MANAGER_ROLE / owner). NOTE: RuleEngine's `ERC3643ComplianceExtendedModule` is intentionally **not** reused for binding — it is an `IERC3643Compliance`, which would drag in transfer-compliance callbacks (`canTransfer`/`transferred`/`created`/`destroyed`) @@ -47,10 +48,11 @@ addressed by a `bytes32` name. `hasRole` override). - **Flexible access control (CMTAT / RuleEngine pattern):** restricted functions use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to - overridable `internal virtual` hooks `_authorizeDocumentManagement()` / - `_authorizeBoundTokenDocumentManagement()` (default `DOCUMENT_MANAGER_ROLE` / - `TOKEN_CONTRACT_ROLE`). Keep the management implementation separate from the - authorization logic — change *who* is authorized by overriding a hook, not by + overridable `internal virtual` hooks `_authorizeDocumentManagement()` (per + deployment: `DOCUMENT_MANAGER_ROLE` / owner) and + `_authorizeBoundTokenDocumentManagement()` (implemented once by + `TokenBindingModule` → allowlist check). Keep the management implementation + separate from the authorization logic — change *who* is authorized via a hook, not by editing the management functions. - **CMTAT integration:** since CMTAT v3, a token uses the engine via CMTAT's `DocumentEngineModule` and `setDocumentEngine(engine)` (reads/writes are forwarded @@ -65,11 +67,11 @@ src/ │ # both management paths, batch functions, modifiers, │ # and the ABSTRACT _authorize* hooks (no access control) ├── DocumentEngine.sol # Deployment #1: role-based access control -│ # (AccessControlEnumerable, the role constants -│ # DOCUMENT_MANAGER_ROLE / TOKEN_CONTRACT_ROLE, _authorize* -│ # impls, hasRole), ERC-2771, supportsInterface, constructor +│ # (AccessControlEnumerable, DOCUMENT_MANAGER_ROLE, +│ # _authorizeDocumentManagement, hasRole), ERC-2771, +│ # supportsInterface, constructor ├── DocumentEngineOwnable.sol # Deployment #2: Ownable2Step (single owner) instead of -│ # roles; owner-managed token binding (ITokenBinding) +│ # roles; document mgmt + binding are owner-only ├── DocumentEngineInvariant.sol # Shared errors only (incl. ERC1643InvalidName / │ # ERC1643MissingDocument); NO access-control specifics ├── interfaces/ @@ -79,8 +81,10 @@ src/ │ └── ITokenBinding.sol # Shared binding surface: bindToken / unbindToken / │ # isTokenBound + TokenBindingSet (both deployments) └── modules/ - └── VersionModule.sol # Version module: implements ERC-8303 version() + ERC-165, - # holds the VERSION constant (currently "0.4.0") + ├── VersionModule.sol # Version module: implements ERC-8303 version() + ERC-165, + │ # holds the VERSION constant (currently "0.4.0") + └── TokenBindingModule.sol # Shared token-binding allowlist (ITokenBinding) + NotBoundToken; + # wires the bound-token hook; used by both deployments script/ ├── DeployDocumentEngine.s.sol # Deploy role-based DocumentEngine (env: DOCUMENT_ENGINE_ADMIN, diff --git a/README.md b/README.md index 134a6b2..a548d8f 100644 --- a/README.md +++ b/README.md @@ -58,13 +58,13 @@ same way regardless of the access-control model: documentEngine.bindToken(address(token)); // also: unbindToken(token), isTokenBound(token) ``` -Under the hood the role-based `DocumentEngine` binds by granting -`TOKEN_CONTRACT_ROLE` (the CMTA [RuleEngine](https://github.com/CMTA/RuleEngine) -pattern) and the `DocumentEngineOwnable` uses an owner-managed allowlist; both -expose the same `bindToken` / `unbindToken` / `isTokenBound` functions and the -`TokenBindingSet` event. (The revert raised when a *non-bound* caller attempts a -write differs — `AccessControlUnauthorizedAccount` vs `NotBoundToken` — since it -comes from each deployment's access-control model.) +Both deployments share the exact same binding mechanism — a single allowlist in +`TokenBindingModule` (`src/modules/TokenBindingModule.sol`), **not** a role. They +expose the same `bindToken` / `unbindToken` / `isTokenBound` functions, emit the +same `TokenBindingSet` event, and revert with the same `NotBoundToken` error when a +non-bound caller attempts a write. The only difference is *who* may bind: whoever +may manage documents in that deployment (the `DOCUMENT_MANAGER_ROLE` holder, or the +`owner`), since binding is authorized by the same document-management hook. Once bound, the token manages its **own** documents (`msg.sender` is the token); it can never affect another contract's documents: @@ -82,24 +82,32 @@ function removeDocument(bytes32 name_) external; ### Flexible access control Following the CMTAT / [RuleEngine](https://github.com/CMTA/RuleEngine) pattern, -the restricted functions do not hardcode a role check. They carry a **modifier** +the restricted functions do not hardcode a check. They carry a **modifier** (`onlyDocumentManager` / `onlyBoundToken`) that delegates to an **overridable `internal virtual` authorization hook**: +- the **admin path** delegates to `_authorizeDocumentManagement()`, the one hook + each deployment implements (`_checkRole(DOCUMENT_MANAGER_ROLE)` for + `DocumentEngine`, `_checkOwner()` for `DocumentEngineOwnable`); +- the **bound-token path** delegates to `_authorizeBoundTokenDocumentManagement()`, + which `TokenBindingModule` implements once for both deployments (it checks the + shared binding allowlist). + ```solidity +// implemented per deployment (the only access-control hook they supply) function _authorizeDocumentManagement() internal view virtual { - _checkRole(DOCUMENT_MANAGER_ROLE); + _checkRole(DOCUMENT_MANAGER_ROLE); // or _checkOwner() } -function _authorizeBoundTokenDocumentManagement() internal view virtual { - _checkRole(TOKEN_CONTRACT_ROLE); +// implemented once in TokenBindingModule for both deployments +function _authorizeBoundTokenDocumentManagement() internal view virtual override { + _checkTokenBound(); // reverts NotBoundToken if msg.sender is not bound } ``` This separates the document-management implementation from the authorization -logic: a subclass can override a hook to change *who* is authorized (e.g. a -different role, an allowlist, or open access) without touching the management -functions. The default behavior is the role checks shown above. +logic: a subclass changes *who* is authorized by overriding the hook, never by +touching the management functions. ### Why not reuse RuleEngine's ERC-3643 compliance module? @@ -172,17 +180,20 @@ The engine is split into two contracts (CMTAT module/deployment pattern): the members of each role on-chain. - **`DocumentEngineOwnable`** (alternative deployment) — same base logic, but access control is a single **owner** via `Ownable2Step` (two-step ownership - transfer) instead of roles. Admin management is `owner`-only; token binding uses - an owner-managed allowlist behind the shared `ITokenBinding` surface. - -Both deployments implement the shared **`ITokenBinding`** interface -(`bindToken` / `unbindToken` / `isTokenBound` + `TokenBindingSet`), so the binding -surface is uniform and ERC-165-discoverable regardless of the access-control model. - -`DocumentEngineInvariant` provides the errors and the optional multi-token events -shared by every deployment. Access-control specifics are **not** defined there: -the role constants (`DOCUMENT_MANAGER_ROLE`, `TOKEN_CONTRACT_ROLE`) live in the -role-based `DocumentEngine`, and the owner/binding logic in `DocumentEngineOwnable`. + transfer) instead of roles. Both document management and token binding are + `owner`-only. +- **`TokenBindingModule`** (`src/modules/TokenBindingModule.sol`) — the shared + token-binding registry (an allowlist) implementing `ITokenBinding` + (`bindToken` / `unbindToken` / `isTokenBound` + `TokenBindingSet`). Both + deployments inherit it, so binding is identical (same functions, event, and + `NotBoundToken` revert) and ERC-165-discoverable regardless of the + access-control model; binding is authorized by each deployment's + document-management hook. + +`DocumentEngineInvariant` provides the errors shared by every deployment. +Access-control specifics are **not** defined there: the `DOCUMENT_MANAGER_ROLE` +constant lives in the role-based `DocumentEngine`, and the owner logic in +`DocumentEngineOwnable`. `VersionModule` (`src/modules/VersionModule.sol`) isolates the version concern and implements [ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index 4dc9701..dd91bd2 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -7,7 +7,7 @@ import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; import {IERC1643MultiDocument} from "./interfaces/IERC1643MultiDocument.sol"; import {ITokenBinding} from "./interfaces/ITokenBinding.sol"; import "OZ/metatx/ERC2771Context.sol"; -import "./DocumentEngineBase.sol"; +import "./modules/TokenBindingModule.sol"; import "./modules/VersionModule.sol"; /** @@ -22,22 +22,17 @@ import "./modules/VersionModule.sol"; * the ERC-2771 (gasless) meta-transaction support. */ contract DocumentEngine is - DocumentEngineBase, + TokenBindingModule, VersionModule, AccessControlEnumerable, - ERC2771Context, - ITokenBinding + ERC2771Context { - // Role allowed to manage documents on behalf of any smart contract (admin path) + // Role allowed to manage documents on behalf of any smart contract, and to + // bind/unbind tokens (admin path). Token binding uses the shared allowlist in + // {TokenBindingModule}, not a dedicated role. bytes32 public constant DOCUMENT_MANAGER_ROLE = keccak256("DOCUMENT_MANAGER_ROLE"); - // Role granted to a token bound to the engine, allowing it to manage its own - // documents through the standard ERC-1643 functions (msg.sender is the token). - // Mirrors the RuleEngine binding pattern (CMTA/RuleEngine `TOKEN_CONTRACT_ROLE`). - bytes32 public constant TOKEN_CONTRACT_ROLE = - keccak256("TOKEN_CONTRACT_ROLE"); - // Constructor to initialize the admin role constructor( address admin, @@ -61,53 +56,6 @@ contract DocumentEngine is _checkRole(DOCUMENT_MANAGER_ROLE); } - /** - * @dev Authorization for the bound-token document-management path. - * The caller must hold `TOKEN_CONTRACT_ROLE` (the RuleEngine binding - * pattern). Override to customize. - */ - function _authorizeBoundTokenDocumentManagement() - internal - view - virtual - override - { - _checkRole(TOKEN_CONTRACT_ROLE); - } - - /* ============ Token binding (ITokenBinding) ============ */ - - /** - * @inheritdoc ITokenBinding - * @dev Binding a token is granting it `TOKEN_CONTRACT_ROLE`. Authorization is - * that of {AccessControl-grantRole} (the role admin of `TOKEN_CONTRACT_ROLE`, - * i.e. `DEFAULT_ADMIN_ROLE` by default). - */ - function bindToken(address token) external override { - grantRole(TOKEN_CONTRACT_ROLE, token); - emit TokenBindingSet(token, true); - } - - /** - * @inheritdoc ITokenBinding - * @dev Unbinding a token is revoking its `TOKEN_CONTRACT_ROLE`. - */ - function unbindToken(address token) external override { - revokeRole(TOKEN_CONTRACT_ROLE, token); - emit TokenBindingSet(token, false); - } - - /** - * @inheritdoc ITokenBinding - * @dev Note: because the default admin holds every role (see {hasRole}), this - * returns `true` for a `DEFAULT_ADMIN_ROLE` holder as well. - */ - function isTokenBound( - address token - ) external view override returns (bool) { - return hasRole(TOKEN_CONTRACT_ROLE, token); - } - /** * @dev Returns `true` if `account` has been granted `role`. The default admin * (`DEFAULT_ADMIN_ROLE`) is treated as holding **every** role. diff --git a/src/DocumentEngineOwnable.sol b/src/DocumentEngineOwnable.sol index c70821d..b0fb598 100644 --- a/src/DocumentEngineOwnable.sol +++ b/src/DocumentEngineOwnable.sol @@ -7,36 +7,26 @@ import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; import {IERC1643MultiDocument} from "./interfaces/IERC1643MultiDocument.sol"; import {ITokenBinding} from "./interfaces/ITokenBinding.sol"; import "OZ/metatx/ERC2771Context.sol"; -import "./DocumentEngineBase.sol"; +import "./modules/TokenBindingModule.sol"; import "./modules/VersionModule.sol"; /** * @title DocumentEngineOwnable * @notice Alternative deployment of the DocumentEngine that uses a single owner * ({Ownable2Step}) instead of role-based access control. - * @dev Reuses the same document-management logic ({DocumentEngineBase}) and only - * swaps the access-control implementation, illustrating the base/deployment - * separation: - * - admin path (`onlyDocumentManager`): restricted to the `owner`; - * - bound-token path (`onlyBoundToken`): restricted to tokens the owner has - * bound to the engine (owner-managed allowlist, the analog of the role-based - * `TOKEN_CONTRACT_ROLE` binding). A bound token manages only its own documents. - * Ownership uses the two-step transfer flow for safety, and the contract also - * exposes its version through ERC-8303 ({VersionModule}) and wires ERC-2771. + * @dev Reuses the same document-management logic ({DocumentEngineBase}) and token + * binding ({TokenBindingModule}), swapping only the access-control implementation: + * document management and token binding are both restricted to the `owner`, and a + * bound token manages only its own documents. Ownership uses the two-step transfer + * flow for safety, and the contract also exposes its version through ERC-8303 + * ({VersionModule}) and wires ERC-2771. */ contract DocumentEngineOwnable is - DocumentEngineBase, + TokenBindingModule, VersionModule, Ownable2Step, - ERC2771Context, - ITokenBinding + ERC2771Context { - /// @dev Tokens bound to the engine, allowed to manage their own documents. - mapping(address => bool) private _boundTokens; - - /// @notice Thrown when a non-bound caller uses the bound-token path. - error NotBoundToken(address caller); - /** * @param owner_ initial owner of the contract * @param forwarderIrrevocable address of the ERC-2771 forwarder (gasless support) @@ -46,59 +36,18 @@ contract DocumentEngineOwnable is address forwarderIrrevocable ) Ownable(owner_) ERC2771Context(forwarderIrrevocable) {} - /*////////////////////////////////////////////////////////////// - TOKEN BINDING (ITokenBinding) - //////////////////////////////////////////////////////////////*/ - - /** - * @inheritdoc ITokenBinding - * @dev Owner-managed analog of granting `TOKEN_CONTRACT_ROLE`. - */ - function bindToken(address token) external override onlyOwner { - _boundTokens[token] = true; - emit TokenBindingSet(token, true); - } - - /** - * @inheritdoc ITokenBinding - * @dev Owner-managed analog of revoking `TOKEN_CONTRACT_ROLE`. - */ - function unbindToken(address token) external override onlyOwner { - _boundTokens[token] = false; - emit TokenBindingSet(token, false); - } - - /// @inheritdoc ITokenBinding - function isTokenBound(address token) external view override returns (bool) { - return _boundTokens[token]; - } - /*////////////////////////////////////////////////////////////// ACCESS CONTROL (implementation) //////////////////////////////////////////////////////////////*/ /** - * @dev Authorization for the admin document-management path: only the owner. + * @dev Authorization for the admin document-management path (and, via + * {TokenBindingModule}, for token binding): only the owner. */ function _authorizeDocumentManagement() internal view virtual override { _checkOwner(); } - /** - * @dev Authorization for the bound-token document-management path: the - * caller must be a token bound by the owner. - */ - function _authorizeBoundTokenDocumentManagement() - internal - view - virtual - override - { - if (!_boundTokens[_msgSender()]) { - revert NotBoundToken(_msgSender()); - } - } - /** * @dev ERC-165 discovery: advertises ERC-1643 and its multi-token extension, * plus the version module (ERC-8303). See {IERC165-supportsInterface}. diff --git a/src/modules/TokenBindingModule.sol b/src/modules/TokenBindingModule.sol new file mode 100644 index 0000000..89dab0a --- /dev/null +++ b/src/modules/TokenBindingModule.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.20; + +import {DocumentEngineBase} from "../DocumentEngineBase.sol"; +import {ITokenBinding} from "../interfaces/ITokenBinding.sol"; + +/** + * @title TokenBindingModule + * @notice Shared token-binding registry (an allowlist) implementing {ITokenBinding}, + * used by every DocumentEngine deployment so binding behaves identically — same + * functions, same event, same revert — regardless of the access-control model. + * @dev A *bound* token may manage its own documents through the standard + * single-argument ERC-1643 functions (`msg.sender` is the token). This module: + * - stores the allowlist and implements `bindToken` / `unbindToken` / `isTokenBound`; + * - wires the base bound-token hook ({_authorizeBoundTokenDocumentManagement}) to + * the allowlist ({_checkTokenBound}); + * - gates binding management with the deployment's document-management + * authorization ({_authorizeDocumentManagement}), so whoever may manage + * documents may also decide bindings. It is therefore access-control agnostic: + * the deployment only implements {_authorizeDocumentManagement}. + */ +abstract contract TokenBindingModule is DocumentEngineBase, ITokenBinding { + /// @dev Tokens bound to the engine, allowed to manage their own documents. + mapping(address => bool) private _boundTokens; + + /// @notice Thrown when a non-bound caller attempts a bound-token operation. + error NotBoundToken(address caller); + + /** + * @inheritdoc ITokenBinding + * @dev Authorized by the deployment's document-management check. + */ + function bindToken(address token) external virtual override { + _authorizeDocumentManagement(); + _boundTokens[token] = true; + emit TokenBindingSet(token, true); + } + + /** + * @inheritdoc ITokenBinding + * @dev Authorized by the deployment's document-management check. + */ + function unbindToken(address token) external virtual override { + _authorizeDocumentManagement(); + _boundTokens[token] = false; + emit TokenBindingSet(token, false); + } + + /// @inheritdoc ITokenBinding + function isTokenBound( + address token + ) public view virtual override returns (bool) { + return _boundTokens[token]; + } + + /** + * @dev Bound-token document-management authorization: the caller + * (`_msgSender()`) must be a bound token. + */ + function _authorizeBoundTokenDocumentManagement() + internal + view + virtual + override + { + _checkTokenBound(); + } + + /// @dev Reverts {NotBoundToken} if the caller (`_msgSender()`) is not bound. + function _checkTokenBound() internal view { + if (!_boundTokens[_msgSender()]) { + revert NotBoundToken(_msgSender()); + } + } +} diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index 7a5a2fc..97e955b 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -9,6 +9,7 @@ import {IERC165} from "OZ/utils/introspection/IERC165.sol"; import {IERC8303} from "../src/interfaces/IERC8303.sol"; import {IERC1643MultiDocument} from "../src/interfaces/IERC1643MultiDocument.sol"; import {ITokenBinding} from "../src/interfaces/ITokenBinding.sol"; +import {TokenBindingModule} from "../src/modules/TokenBindingModule.sol"; import {DocumentEngineModule} from "CMTAT/modules/wrapper/options/DocumentEngineModule.sol"; /** @@ -56,8 +57,6 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { // shared DocumentEngineInvariant; mirrored here for the assertions. bytes32 public constant DOCUMENT_MANAGER_ROLE = keccak256("DOCUMENT_MANAGER_ROLE"); - bytes32 public constant TOKEN_CONTRACT_ROLE = - keccak256("TOKEN_CONTRACT_ROLE"); address AddressZero = address(0); function setUp() public { @@ -229,7 +228,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { } /*////////////////////////////////////////////////////////////// - Bound token (RuleEngine binding pattern, TOKEN_CONTRACT_ROLE) + Bound token (shared ITokenBinding allowlist / TokenBindingModule) //////////////////////////////////////////////////////////////*/ function testBoundTokenCanManageOwnDocument() public { @@ -269,7 +268,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { abi.encodeWithSelector( AccessControlUnauthorizedAccount.selector, attacker, - DEFAULT_ADMIN_ROLE + DOCUMENT_MANAGER_ROLE ) ); documentEngine.bindToken(testContract); @@ -290,9 +289,8 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { vm.prank(attacker); vm.expectRevert( abi.encodeWithSelector( - AccessControlUnauthorizedAccount.selector, - attacker, - TOKEN_CONTRACT_ROLE + TokenBindingModule.NotBoundToken.selector, + attacker ) ); documentEngine.setDocument(selfName, documentURI, documentHash); @@ -303,9 +301,8 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { vm.prank(attacker); vm.expectRevert( abi.encodeWithSelector( - AccessControlUnauthorizedAccount.selector, - attacker, - TOKEN_CONTRACT_ROLE + TokenBindingModule.NotBoundToken.selector, + attacker ) ); documentEngine.removeDocument(selfName); diff --git a/test/DocumentEngineOwnable.t.sol b/test/DocumentEngineOwnable.t.sol index 5d8ae80..ae6ea06 100644 --- a/test/DocumentEngineOwnable.t.sol +++ b/test/DocumentEngineOwnable.t.sol @@ -10,6 +10,7 @@ import {IERC8303} from "../src/interfaces/IERC8303.sol"; import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; import {IERC1643MultiDocument} from "../src/interfaces/IERC1643MultiDocument.sol"; import {ITokenBinding} from "../src/interfaces/ITokenBinding.sol"; +import {TokenBindingModule} from "../src/modules/TokenBindingModule.sol"; contract DocumentEngineOwnableTest is Test { DocumentEngineOwnable public engine; @@ -121,7 +122,7 @@ contract DocumentEngineOwnableTest is Test { vm.prank(testContract); vm.expectRevert( abi.encodeWithSelector( - DocumentEngineOwnable.NotBoundToken.selector, + TokenBindingModule.NotBoundToken.selector, testContract ) ); @@ -132,7 +133,7 @@ contract DocumentEngineOwnableTest is Test { vm.prank(attacker); vm.expectRevert( abi.encodeWithSelector( - DocumentEngineOwnable.NotBoundToken.selector, + TokenBindingModule.NotBoundToken.selector, attacker ) ); From ddc0881ea36b2ecc62916679f30cfef06e44fb60 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 18:34:18 +0200 Subject: [PATCH 16/47] style: adopt forge fmt as the canonical formatter (drop prettier), reformat sources --- CHANGELOG.md | 4 +- README.md | 8 +- foundry.toml | 9 + package.json | 1 - script/DeployDocumentEngine.s.sol | 5 +- script/DeployDocumentEngineOwnable.s.sol | 10 +- src/DocumentEngine.sol | 58 ++---- src/DocumentEngineBase.sol | 108 +++------- src/DocumentEngineOwnable.sol | 44 +---- src/interfaces/IERC1643MultiDocument.sol | 30 +-- src/modules/TokenBindingModule.sol | 11 +- src/modules/VersionModule.sol | 16 +- test/DocumentEngine.t.sol | 242 +++++------------------ test/DocumentEngineOwnable.t.sol | 46 +---- 14 files changed, 131 insertions(+), 461 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9205be..a6266bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,9 +30,9 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) > Before a new release, perform the following tasks - Code: Update the version name in the `Version` core module, variable VERSION -- Run linter +- Run the formatter -> npm run-script lint:all:prettier +> forge fmt - Documentation - Perform a code coverage and update the files in the corresponding directory [./doc/general/test/coverage](./doc/general/test/coverage) diff --git a/README.md b/README.md index a548d8f..14801ce 100644 --- a/README.md +++ b/README.md @@ -296,10 +296,14 @@ The toolchain includes the following components, where the versions are the late ## Tools -### Prettier +### Formatting (forge fmt) + +`forge fmt` is the canonical formatter for this project (configured under `[fmt]` +in `foundry.toml`): ```bash -npx prettier --write --plugin=prettier-plugin-solidity 'src/**/*.sol' +forge fmt # format src/, test/, script/ +forge fmt --check # verify formatting (CI) ``` ### Slither diff --git a/foundry.toml b/foundry.toml index 8945d0e..67b08dd 100644 --- a/foundry.toml +++ b/foundry.toml @@ -7,4 +7,13 @@ optimizer = true optimizer_runs = 200 evm_version = 'prague' +# `forge fmt` is the canonical formatter for this project (run `forge fmt`). +[fmt] +line_length = 120 +tab_width = 4 +bracket_spacing = false +int_types = "long" +quote_style = "double" +number_underscore = "preserve" + # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/package.json b/package.json index a56a8e3..4ca5410 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,5 @@ { "devDependencies": { - "prettier-plugin-solidity": "^1.4.1", "solidity-docgen": "^0.6.0-beta.36", "surya": "^0.4.11" } diff --git a/script/DeployDocumentEngine.s.sol b/script/DeployDocumentEngine.s.sol index b778daf..d76e75f 100644 --- a/script/DeployDocumentEngine.s.sol +++ b/script/DeployDocumentEngine.s.sol @@ -34,10 +34,7 @@ contract DeployDocumentEngine is Script { } /// @dev Broadcasted deployment, isolated from env parsing so it can be reused/tested. - function deploy( - address admin, - address forwarder - ) public returns (DocumentEngine documentEngine) { + function deploy(address admin, address forwarder) public returns (DocumentEngine documentEngine) { vm.startBroadcast(); documentEngine = new DocumentEngine(admin, forwarder); vm.stopBroadcast(); diff --git a/script/DeployDocumentEngineOwnable.s.sol b/script/DeployDocumentEngineOwnable.s.sol index 5556474..e7b7e92 100644 --- a/script/DeployDocumentEngineOwnable.s.sol +++ b/script/DeployDocumentEngineOwnable.s.sol @@ -27,20 +27,14 @@ contract DeployDocumentEngineOwnable is Script { documentEngine = deploy(owner, forwarder); - console2.log( - "DocumentEngineOwnable deployed at:", - address(documentEngine) - ); + console2.log("DocumentEngineOwnable deployed at:", address(documentEngine)); console2.log(" owner :", owner); console2.log(" trusted forwarder:", forwarder); console2.log(" version :", documentEngine.version()); } /// @dev Broadcasted deployment, isolated from env parsing so it can be reused/tested. - function deploy( - address owner, - address forwarder - ) public returns (DocumentEngineOwnable documentEngine) { + function deploy(address owner, address forwarder) public returns (DocumentEngineOwnable documentEngine) { vm.startBroadcast(); documentEngine = new DocumentEngineOwnable(owner, forwarder); vm.stopBroadcast(); diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index dd91bd2..7b1a17e 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -21,23 +21,14 @@ import "./modules/VersionModule.sol"; * version is exposed through the {VersionModule} (ERC-8303), and it also wires * the ERC-2771 (gasless) meta-transaction support. */ -contract DocumentEngine is - TokenBindingModule, - VersionModule, - AccessControlEnumerable, - ERC2771Context -{ +contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnumerable, ERC2771Context { // Role allowed to manage documents on behalf of any smart contract, and to // bind/unbind tokens (admin path). Token binding uses the shared allowlist in // {TokenBindingModule}, not a dedicated role. - bytes32 public constant DOCUMENT_MANAGER_ROLE = - keccak256("DOCUMENT_MANAGER_ROLE"); + bytes32 public constant DOCUMENT_MANAGER_ROLE = keccak256("DOCUMENT_MANAGER_ROLE"); // Constructor to initialize the admin role - constructor( - address admin, - address forwarderIrrevocable - ) ERC2771Context(forwarderIrrevocable) { + constructor(address admin, address forwarderIrrevocable) ERC2771Context(forwarderIrrevocable) { if (admin == address(0)) { revert AdminWithAddressZeroNotAllowed(); } @@ -65,10 +56,13 @@ contract DocumentEngine is * report only explicit grants, so a `DEFAULT_ADMIN_ROLE` holder satisfies * `hasRole(anyRole, admin)` yet does not appear in `getRoleMember(anyRole, ...)`. */ - function hasRole( - bytes32 role, - address account - ) public view virtual override(AccessControl, IAccessControl) returns (bool) { + function hasRole(bytes32 role, address account) + public + view + virtual + override(AccessControl, IAccessControl) + returns (bool) + { // The Default Admin has all roles if (super.hasRole(DEFAULT_ADMIN_ROLE, account)) { return true; @@ -84,20 +78,15 @@ contract DocumentEngine is * extension, so it advertises `type(IERC1643MultiDocument).interfaceId`. * See {IERC165-supportsInterface}. */ - function supportsInterface( - bytes4 interfaceId - ) + function supportsInterface(bytes4 interfaceId) public view virtual override(VersionModule, AccessControlEnumerable) returns (bool) { - return - interfaceId == type(IERC1643).interfaceId || - interfaceId == type(IERC1643MultiDocument).interfaceId || - interfaceId == type(ITokenBinding).interfaceId || - super.supportsInterface(interfaceId); + return interfaceId == type(IERC1643).interfaceId || interfaceId == type(IERC1643MultiDocument).interfaceId + || interfaceId == type(ITokenBinding).interfaceId || super.supportsInterface(interfaceId); } /*////////////////////////////////////////////////////////////// @@ -107,36 +96,21 @@ contract DocumentEngine is /** * @dev This surcharge is not necessary if you do not use ERC2771 */ - function _msgSender() - internal - view - override(ERC2771Context, Context) - returns (address sender) - { + function _msgSender() internal view override(ERC2771Context, Context) returns (address sender) { return ERC2771Context._msgSender(); } /** * @dev This surcharge is not necessary if you do not use ERC2771 */ - function _msgData() - internal - view - override(ERC2771Context, Context) - returns (bytes calldata) - { + function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata) { return ERC2771Context._msgData(); } /** * @dev This surcharge is not necessary if you do not use the MetaTxModule */ - function _contextSuffixLength() - internal - view - override(ERC2771Context, Context) - returns (uint256) - { + function _contextSuffixLength() internal view override(ERC2771Context, Context) returns (uint256) { return ERC2771Context._contextSuffixLength(); } } diff --git a/src/DocumentEngineBase.sol b/src/DocumentEngineBase.sol index 4796066..f104daf 100644 --- a/src/DocumentEngineBase.sol +++ b/src/DocumentEngineBase.sol @@ -19,12 +19,7 @@ import "./DocumentEngineInvariant.sol"; * This separation (base logic + deployment-defined access control) follows the * CMTAT and CMTA/RuleEngine pattern. */ -abstract contract DocumentEngineBase is - IERC1643, - IERC1643MultiDocument, - DocumentEngineInvariant, - Context -{ +abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, DocumentEngineInvariant, Context { // Mapping from contract addresses to document names to their corresponding Document structs mapping(address => mapping(bytes32 => Document)) private _documents; mapping(address => bytes32[]) private _documentNames; @@ -73,22 +68,18 @@ abstract contract DocumentEngineBase is /** * @notice Restricted function to set or update a document */ - function setDocument( - address subject, - bytes32 name_, - string memory uri_, - bytes32 documentHash_ - ) public override onlyDocumentManager { + function setDocument(address subject, bytes32 name_, string memory uri_, bytes32 documentHash_) + public + override + onlyDocumentManager + { _setDocument(subject, name_, uri_, documentHash_); } /** * @notice Restricted function to remove a document for a given smart contract and name */ - function removeDocument( - address subject, - bytes32 name_ - ) external override onlyDocumentManager { + function removeDocument(address subject, bytes32 name_) external override onlyDocumentManager { _removeDocument(subject, name_); } @@ -102,11 +93,7 @@ abstract contract DocumentEngineBase is * {_authorizeBoundTokenDocumentManagement} implementations). A bound token can * only manage its own documents; it can never affect another contract's documents. */ - function setDocument( - bytes32 name_, - string calldata uri_, - bytes32 documentHash_ - ) external override onlyBoundToken { + function setDocument(bytes32 name_, string calldata uri_, bytes32 documentHash_) external override onlyBoundToken { _setDocument(_msgSender(), name_, uri_, documentHash_); } @@ -114,9 +101,7 @@ abstract contract DocumentEngineBase is * @notice ERC-1643 function to remove a document for the caller. * @dev See {setDocument}. Scoped to the caller (`_msgSender()`) namespace. */ - function removeDocument( - bytes32 name_ - ) external override onlyBoundToken { + function removeDocument(bytes32 name_) external override onlyBoundToken { _removeDocument(_msgSender(), name_); } @@ -130,10 +115,8 @@ abstract contract DocumentEngineBase is bytes32[] calldata hashes ) external onlyDocumentManager { if ( - subjects.length == 0 || - subjects.length != names.length || - names.length != uris.length || - uris.length != hashes.length + subjects.length == 0 || subjects.length != names.length || names.length != uris.length + || uris.length != hashes.length ) { revert InvalidInputLength(); } @@ -151,11 +134,7 @@ abstract contract DocumentEngineBase is string[] calldata uris, bytes32[] calldata hashes ) external onlyDocumentManager { - if ( - names.length == 0 || - names.length != uris.length || - uris.length != hashes.length - ) { + if (names.length == 0 || names.length != uris.length || uris.length != hashes.length) { revert InvalidInputLength(); } for (uint256 i = 0; i < names.length; ++i) { @@ -166,14 +145,8 @@ abstract contract DocumentEngineBase is /** * @notice Batch version of removeDocument to handle multiple documents at once */ - function batchRemoveDocuments( - address[] calldata subjects, - bytes32[] calldata names - ) external onlyDocumentManager { - if ( - subjects.length == 0 || - (subjects.length != names.length) - ) { + function batchRemoveDocuments(address[] calldata subjects, bytes32[] calldata names) external onlyDocumentManager { + if (subjects.length == 0 || (subjects.length != names.length)) { revert InvalidInputLength(); } @@ -185,10 +158,7 @@ abstract contract DocumentEngineBase is /** * @notice Batch version of removeDocument to handle multiple documents at once */ - function batchRemoveDocuments( - address subject, - bytes32[] calldata names - ) external onlyDocumentManager { + function batchRemoveDocuments(address subject, bytes32[] calldata names) external onlyDocumentManager { if (names.length == 0) { revert InvalidInputLength(); } @@ -201,40 +171,28 @@ abstract contract DocumentEngineBase is /** * @notice ERC-1643 function to get a document for the caller (`_msgSender()`) */ - function getDocument( - bytes32 name_ - ) external view override returns (Document memory) { + function getDocument(bytes32 name_) external view override returns (Document memory) { return _getDocument(_msgSender(), name_); } /** * @notice Public function to get a document for a specific contract address */ - function getDocument( - address subject, - bytes32 name_ - ) external view override returns (Document memory) { + function getDocument(address subject, bytes32 name_) external view override returns (Document memory) { return _getDocument(subject, name_); } /** * @notice Get all document names for msg.sender */ - function getAllDocuments() - external - view - override - returns (bytes32[] memory) - { + function getAllDocuments() external view override returns (bytes32[] memory) { return _documentNames[_msgSender()]; } /** * @notice Get all document names for a specific smart contract */ - function getAllDocuments( - address subject - ) external view override returns (bytes32[] memory) { + function getAllDocuments(address subject) external view override returns (bytes32[] memory) { return _documentNames[subject]; } @@ -245,26 +203,18 @@ abstract contract DocumentEngineBase is /** * @dev Internal function to fetch a document */ - function _getDocument( - address subject, - bytes32 name_ - ) internal view returns (Document memory) { + function _getDocument(address subject, bytes32 name_) internal view returns (Document memory) { return _documents[subject][name_]; } /** * @dev Internal helper to remove the document name from the list of document names */ - function _removeDocumentName( - address subject, - bytes32 name_ - ) internal { + function _removeDocumentName(address subject, bytes32 name_) internal { uint256 length = _documentNames[subject].length; for (uint256 i = 0; i < length; ++i) { if (_documentNames[subject][i] == name_) { - _documentNames[subject][i] = _documentNames[ - subject - ][length - 1]; + _documentNames[subject][i] = _documentNames[subject][length - 1]; _documentNames[subject].pop(); break; } @@ -282,23 +232,13 @@ abstract contract DocumentEngineBase is // "Emission Responsibility" rules it emits only the address-carrying // extension event (the base `DocumentRemoved` is the token contract's // responsibility). See doc/ERCSpecification. - emit DocumentRemovedForSubject( - subject, - name_, - doc.uri, - doc.documentHash - ); + emit DocumentRemovedForSubject(subject, name_, doc.uri, doc.documentHash); delete _documents[subject][name_]; _removeDocumentName(subject, name_); } - function _setDocument( - address subject, - bytes32 name_, - string memory uri_, - bytes32 documentHash_ - ) internal { + function _setDocument(address subject, bytes32 name_, string memory uri_, bytes32 documentHash_) internal { // ERC-1643: reject the null name (ambiguous / default key) if (name_ == bytes32(0)) { revert ERC1643InvalidName(); diff --git a/src/DocumentEngineOwnable.sol b/src/DocumentEngineOwnable.sol index b0fb598..e8227cc 100644 --- a/src/DocumentEngineOwnable.sol +++ b/src/DocumentEngineOwnable.sol @@ -21,20 +21,12 @@ import "./modules/VersionModule.sol"; * flow for safety, and the contract also exposes its version through ERC-8303 * ({VersionModule}) and wires ERC-2771. */ -contract DocumentEngineOwnable is - TokenBindingModule, - VersionModule, - Ownable2Step, - ERC2771Context -{ +contract DocumentEngineOwnable is TokenBindingModule, VersionModule, Ownable2Step, ERC2771Context { /** * @param owner_ initial owner of the contract * @param forwarderIrrevocable address of the ERC-2771 forwarder (gasless support) */ - constructor( - address owner_, - address forwarderIrrevocable - ) Ownable(owner_) ERC2771Context(forwarderIrrevocable) {} + constructor(address owner_, address forwarderIrrevocable) Ownable(owner_) ERC2771Context(forwarderIrrevocable) {} /*////////////////////////////////////////////////////////////// ACCESS CONTROL (implementation) @@ -52,14 +44,9 @@ contract DocumentEngineOwnable is * @dev ERC-165 discovery: advertises ERC-1643 and its multi-token extension, * plus the version module (ERC-8303). See {IERC165-supportsInterface}. */ - function supportsInterface( - bytes4 interfaceId - ) public view virtual override(VersionModule) returns (bool) { - return - interfaceId == type(IERC1643).interfaceId || - interfaceId == type(IERC1643MultiDocument).interfaceId || - interfaceId == type(ITokenBinding).interfaceId || - super.supportsInterface(interfaceId); + function supportsInterface(bytes4 interfaceId) public view virtual override(VersionModule) returns (bool) { + return interfaceId == type(IERC1643).interfaceId || interfaceId == type(IERC1643MultiDocument).interfaceId + || interfaceId == type(ITokenBinding).interfaceId || super.supportsInterface(interfaceId); } /*////////////////////////////////////////////////////////////// @@ -69,36 +56,21 @@ contract DocumentEngineOwnable is /** * @dev This surcharge is not necessary if you do not use ERC2771 */ - function _msgSender() - internal - view - override(ERC2771Context, Context) - returns (address sender) - { + function _msgSender() internal view override(ERC2771Context, Context) returns (address sender) { return ERC2771Context._msgSender(); } /** * @dev This surcharge is not necessary if you do not use ERC2771 */ - function _msgData() - internal - view - override(ERC2771Context, Context) - returns (bytes calldata) - { + function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata) { return ERC2771Context._msgData(); } /** * @dev This surcharge is not necessary if you do not use the MetaTxModule */ - function _contextSuffixLength() - internal - view - override(ERC2771Context, Context) - returns (uint256) - { + function _contextSuffixLength() internal view override(ERC2771Context, Context) returns (uint256) { return ERC2771Context._contextSuffixLength(); } } diff --git a/src/interfaces/IERC1643MultiDocument.sol b/src/interfaces/IERC1643MultiDocument.sol index 2173ef4..f0dbd42 100644 --- a/src/interfaces/IERC1643MultiDocument.sol +++ b/src/interfaces/IERC1643MultiDocument.sol @@ -16,42 +16,22 @@ import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; */ interface IERC1643MultiDocument { /// @notice Returns metadata for the document `name` belonging to `subject`. - function getDocument( - address subject, - bytes32 name - ) external view returns (IERC1643.Document memory document); + function getDocument(address subject, bytes32 name) external view returns (IERC1643.Document memory document); /// @notice Returns all document names currently tracked for `subject`. - function getAllDocuments( - address subject - ) external view returns (bytes32[] memory documentNames); + function getAllDocuments(address subject) external view returns (bytes32[] memory documentNames); /// @notice Creates or updates a document entry for `subject`. /// @dev MUST emit {DocumentUpdatedForSubject} on success. - function setDocument( - address subject, - bytes32 name, - string calldata uri, - bytes32 documentHash - ) external; + function setDocument(address subject, bytes32 name, string calldata uri, bytes32 documentHash) external; /// @notice Removes an existing document entry for `subject`. /// @dev MUST emit {DocumentRemovedForSubject} on success. function removeDocument(address subject, bytes32 name) external; /// @notice Emitted when a document is created or updated for `subject`. - event DocumentUpdatedForSubject( - address indexed subject, - bytes32 indexed name, - string uri, - bytes32 documentHash - ); + event DocumentUpdatedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); /// @notice Emitted when a document is removed for `subject`. - event DocumentRemovedForSubject( - address indexed subject, - bytes32 indexed name, - string uri, - bytes32 documentHash - ); + event DocumentRemovedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); } diff --git a/src/modules/TokenBindingModule.sol b/src/modules/TokenBindingModule.sol index 89dab0a..245fddc 100644 --- a/src/modules/TokenBindingModule.sol +++ b/src/modules/TokenBindingModule.sol @@ -47,9 +47,7 @@ abstract contract TokenBindingModule is DocumentEngineBase, ITokenBinding { } /// @inheritdoc ITokenBinding - function isTokenBound( - address token - ) public view virtual override returns (bool) { + function isTokenBound(address token) public view virtual override returns (bool) { return _boundTokens[token]; } @@ -57,12 +55,7 @@ abstract contract TokenBindingModule is DocumentEngineBase, ITokenBinding { * @dev Bound-token document-management authorization: the caller * (`_msgSender()`) must be a bound token. */ - function _authorizeBoundTokenDocumentManagement() - internal - view - virtual - override - { + function _authorizeBoundTokenDocumentManagement() internal view virtual override { _checkTokenBound(); } diff --git a/src/modules/VersionModule.sol b/src/modules/VersionModule.sol index 86ce656..e0957f6 100644 --- a/src/modules/VersionModule.sol +++ b/src/modules/VersionModule.sol @@ -23,13 +23,7 @@ abstract contract VersionModule is IERC8303, ERC165 { /** * @inheritdoc IERC8303 */ - function version() - public - view - virtual - override(IERC8303) - returns (string memory version_) - { + function version() public view virtual override(IERC8303) returns (string memory version_) { return VERSION; } @@ -37,11 +31,7 @@ abstract contract VersionModule is IERC8303, ERC165 { * @dev Advertises ERC-8303 support (interface id `0x54fd4d50`). * See {IERC165-supportsInterface}. */ - function supportsInterface( - bytes4 interfaceId - ) public view virtual override returns (bool) { - return - interfaceId == type(IERC8303).interfaceId || - super.supportsInterface(interfaceId); + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IERC8303).interfaceId || super.supportsInterface(interfaceId); } } diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index 97e955b..b05af5b 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -32,10 +32,7 @@ contract CMTATDocumentEngineMock is DocumentEngineModule { * document-management implementation. */ contract OpenDocumentEngine is DocumentEngine { - constructor( - address admin, - address forwarder - ) DocumentEngine(admin, forwarder) {} + constructor(address admin, address forwarder) DocumentEngine(admin, forwarder) {} function _authorizeDocumentManagement() internal view override { // no access restriction (custom authorization) @@ -55,19 +52,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { bytes32 public constant DOCUMENT_ROLE = keccak256("DOCUMENT_ROLE"); // Roles are defined on the role-based deployment (DocumentEngine), not on the // shared DocumentEngineInvariant; mirrored here for the assertions. - bytes32 public constant DOCUMENT_MANAGER_ROLE = - keccak256("DOCUMENT_MANAGER_ROLE"); + bytes32 public constant DOCUMENT_MANAGER_ROLE = keccak256("DOCUMENT_MANAGER_ROLE"); address AddressZero = address(0); function setUp() public { documentEngine = new DocumentEngine(admin, AddressZero); vm.prank(admin); - documentEngine.setDocument( - testContract, - documentName, - documentURI, - documentHash - ); + documentEngine.setDocument(testContract, documentName, documentURI, documentHash); } /*////////////////////////////////////////////////////////////// @@ -81,9 +72,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { // Forwarder assertEq(documentEngine.isTrustedForwarder(forwarder), true); // admin - vm.expectRevert( - abi.encodeWithSelector(AdminWithAddressZeroNotAllowed.selector) - ); + vm.expectRevert(abi.encodeWithSelector(AdminWithAddressZeroNotAllowed.selector)); documentEngine = new DocumentEngine(AddressZero, forwarder); } @@ -94,28 +83,15 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { function testCannotNonAdminSetDocument() public { vm.prank(attacker); vm.expectRevert( - abi.encodeWithSelector( - AccessControlUnauthorizedAccount.selector, - attacker, - DOCUMENT_MANAGER_ROLE - ) - ); - documentEngine.setDocument( - testContract, - documentName, - documentURI, - documentHash + abi.encodeWithSelector(AccessControlUnauthorizedAccount.selector, attacker, DOCUMENT_MANAGER_ROLE) ); + documentEngine.setDocument(testContract, documentName, documentURI, documentHash); } function testCannotNonAdminRemoveDocument() public { vm.prank(attacker); vm.expectRevert( - abi.encodeWithSelector( - AccessControlUnauthorizedAccount.selector, - attacker, - DOCUMENT_MANAGER_ROLE - ) + abi.encodeWithSelector(AccessControlUnauthorizedAccount.selector, attacker, DOCUMENT_MANAGER_ROLE) ); documentEngine.removeDocument(testContract, documentName); } @@ -139,21 +115,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { vm.prank(attacker); vm.expectRevert( - abi.encodeWithSelector( - AccessControlUnauthorizedAccount.selector, - attacker, - DOCUMENT_MANAGER_ROLE - ) + abi.encodeWithSelector(AccessControlUnauthorizedAccount.selector, attacker, DOCUMENT_MANAGER_ROLE) ); documentEngine.batchSetDocuments(smartContracts, names, uris, hashes); vm.prank(attacker); vm.expectRevert( - abi.encodeWithSelector( - AccessControlUnauthorizedAccount.selector, - attacker, - DOCUMENT_MANAGER_ROLE - ) + abi.encodeWithSelector(AccessControlUnauthorizedAccount.selector, attacker, DOCUMENT_MANAGER_ROLE) ); documentEngine.batchSetDocuments(testContract, names, uris, hashes); } @@ -169,21 +137,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { vm.prank(attacker); vm.expectRevert( - abi.encodeWithSelector( - AccessControlUnauthorizedAccount.selector, - attacker, - DOCUMENT_MANAGER_ROLE - ) + abi.encodeWithSelector(AccessControlUnauthorizedAccount.selector, attacker, DOCUMENT_MANAGER_ROLE) ); documentEngine.batchRemoveDocuments(smartContracts, names); vm.prank(attacker); vm.expectRevert( - abi.encodeWithSelector( - AccessControlUnauthorizedAccount.selector, - attacker, - DOCUMENT_MANAGER_ROLE - ) + abi.encodeWithSelector(AccessControlUnauthorizedAccount.selector, attacker, DOCUMENT_MANAGER_ROLE) ); documentEngine.batchRemoveDocuments(testContract, names); } @@ -209,12 +169,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { uint256 lastModif = block.timestamp; vm.prank(admin); - documentEngine.setDocument( - address(cmtat), - documentName, - documentURI, - documentHash - ); + documentEngine.setDocument(address(cmtat), documentName, documentURI, documentHash); // Call from CMTAT, forwarded to the engine bytes32[] memory docs = cmtat.getAllDocuments(); @@ -245,10 +200,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { vm.prank(testContract); documentEngine.setDocument(selfName, selfURI, selfHash); - IERC1643.Document memory doc = documentEngine.getDocument( - testContract, - selfName - ); + IERC1643.Document memory doc = documentEngine.getDocument(testContract, selfName); assertEq(doc.uri, selfURI); assertEq(doc.documentHash, selfHash); assertEq(doc.lastModified, block.timestamp); @@ -265,11 +217,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { function testNonAdminCannotBindToken() public { vm.prank(attacker); vm.expectRevert( - abi.encodeWithSelector( - AccessControlUnauthorizedAccount.selector, - attacker, - DOCUMENT_MANAGER_ROLE - ) + abi.encodeWithSelector(AccessControlUnauthorizedAccount.selector, attacker, DOCUMENT_MANAGER_ROLE) ); documentEngine.bindToken(testContract); } @@ -287,24 +235,14 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { function testUnboundContractCannotSetOwnDocument() public { bytes32 selfName = keccak256("self-doc"); vm.prank(attacker); - vm.expectRevert( - abi.encodeWithSelector( - TokenBindingModule.NotBoundToken.selector, - attacker - ) - ); + vm.expectRevert(abi.encodeWithSelector(TokenBindingModule.NotBoundToken.selector, attacker)); documentEngine.setDocument(selfName, documentURI, documentHash); } function testUnboundContractCannotRemoveOwnDocument() public { bytes32 selfName = keccak256("self-doc"); vm.prank(attacker); - vm.expectRevert( - abi.encodeWithSelector( - TokenBindingModule.NotBoundToken.selector, - attacker - ) - ); + vm.expectRevert(abi.encodeWithSelector(TokenBindingModule.NotBoundToken.selector, attacker)); documentEngine.removeDocument(selfName); } @@ -313,25 +251,14 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { //////////////////////////////////////////////////////////////*/ function testFlexibleAuthorizationCanBeOverridden() public { - OpenDocumentEngine openEngine = new OpenDocumentEngine( - admin, - AddressZero - ); + OpenDocumentEngine openEngine = new OpenDocumentEngine(admin, AddressZero); // attacker holds no role, yet can manage documents because the // authorization hook was overridden to allow anyone. vm.prank(attacker); - openEngine.setDocument( - testContract, - documentName, - documentURI, - documentHash - ); + openEngine.setDocument(testContract, documentName, documentURI, documentHash); - IERC1643.Document memory doc = openEngine.getDocument( - testContract, - documentName - ); + IERC1643.Document memory doc = openEngine.getDocument(testContract, documentName); assertEq(doc.uri, documentURI); assertEq(doc.documentHash, documentHash); } @@ -351,24 +278,16 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { function testSupportsInterfaceERC8303() public { // interface id declared by ERC-8303 assertEq(type(IERC8303).interfaceId, bytes4(0x54fd4d50)); - assertTrue( - documentEngine.supportsInterface(type(IERC8303).interfaceId) - ); + assertTrue(documentEngine.supportsInterface(type(IERC8303).interfaceId)); } function testSupportsERC1643Interfaces() public { // implements the base single-argument functions... assertTrue(documentEngine.supportsInterface(type(IERC1643).interfaceId)); // ...and the address-scoped multi-token extension - assertTrue( - documentEngine.supportsInterface( - type(IERC1643MultiDocument).interfaceId - ) - ); + assertTrue(documentEngine.supportsInterface(type(IERC1643MultiDocument).interfaceId)); // ...and the shared token-binding surface - assertTrue( - documentEngine.supportsInterface(type(ITokenBinding).interfaceId) - ); + assertTrue(documentEngine.supportsInterface(type(ITokenBinding).interfaceId)); } /*////////////////////////////////////////////////////////////// @@ -377,22 +296,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { function testCannotSetDocumentWithZeroName() public { vm.prank(admin); - vm.expectRevert( - abi.encodeWithSelector(ERC1643InvalidName.selector) - ); - documentEngine.setDocument( - testContract, - bytes32(0), - documentURI, - documentHash - ); + vm.expectRevert(abi.encodeWithSelector(ERC1643InvalidName.selector)); + documentEngine.setDocument(testContract, bytes32(0), documentURI, documentHash); } function testCannotRemoveMissingDocument() public { vm.prank(admin); - vm.expectRevert( - abi.encodeWithSelector(ERC1643MissingDocument.selector) - ); + vm.expectRevert(abi.encodeWithSelector(ERC1643MissingDocument.selector)); documentEngine.removeDocument(testContract, keccak256("does-not-exist")); } @@ -400,19 +310,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { vm.prank(admin); documentEngine.bindToken(testContract); vm.prank(testContract); - vm.expectRevert( - abi.encodeWithSelector(ERC1643InvalidName.selector) - ); + vm.expectRevert(abi.encodeWithSelector(ERC1643InvalidName.selector)); documentEngine.setDocument(bytes32(0), documentURI, documentHash); } function testSupportsInterfaceERC165AndAccessControl() public { - assertTrue( - documentEngine.supportsInterface(type(IERC165).interfaceId) - ); - assertTrue( - documentEngine.supportsInterface(type(IAccessControl).interfaceId) - ); + assertTrue(documentEngine.supportsInterface(type(IERC165).interfaceId)); + assertTrue(documentEngine.supportsInterface(type(IAccessControl).interfaceId)); } function testDoesNotSupportInvalidInterface() public { @@ -425,17 +329,9 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { function testAdminCanSetDocument() public { uint256 lastModif = block.timestamp; vm.prank(admin); - documentEngine.setDocument( - testContract, - documentName, - documentURI, - documentHash - ); + documentEngine.setDocument(testContract, documentName, documentURI, documentHash); - IERC1643.Document memory doc = documentEngine.getDocument( - testContract, - documentName - ); + IERC1643.Document memory doc = documentEngine.getDocument(testContract, documentName); assertEq(doc.uri, documentURI); assertEq(doc.documentHash, documentHash); assertEq(doc.lastModified, lastModif); @@ -444,12 +340,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { function testAdminCanSetDocumentAgain() public { // Arrange vm.prank(admin); - documentEngine.setDocument( - testContract, - documentName, - documentURI, - documentHash - ); + documentEngine.setDocument(testContract, documentName, documentURI, documentHash); bytes32[] memory docs = documentEngine.getAllDocuments(testContract); assertEq(docs.length, 1); assertEq(docs[0], documentName); @@ -458,18 +349,10 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { string memory documentURIV2 = "https://example.com/doc1"; bytes32 documentHashV2 = keccak256("doc1Hash"); vm.prank(admin); - documentEngine.setDocument( - testContract, - documentName, - documentURIV2, - documentHashV2 - ); + documentEngine.setDocument(testContract, documentName, documentURIV2, documentHashV2); // Assert - IERC1643.Document memory doc = documentEngine.getDocument( - testContract, - documentName - ); + IERC1643.Document memory doc = documentEngine.getDocument(testContract, documentName); assertEq(doc.uri, documentURIV2); assertEq(doc.documentHash, documentHashV2); assertEq(doc.lastModified, lastModif); @@ -499,19 +382,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentEngine.batchSetDocuments(smartContracts, names, uris, hashes); // Check the first document - IERC1643.Document memory doc1 = documentEngine.getDocument( - testContract, - documentName - ); + IERC1643.Document memory doc1 = documentEngine.getDocument(testContract, documentName); assertEq(doc1.uri, documentURI); assertEq(doc1.documentHash, documentHash); assertEq(doc1.lastModified, block.timestamp); // Check the second document - IERC1643.Document memory doc2 = documentEngine.getDocument( - anotherSmartContract, - names[1] - ); + IERC1643.Document memory doc2 = documentEngine.getDocument(anotherSmartContract, names[1]); assertEq(doc2.uri, uris[1]); assertEq(doc2.documentHash, hashes[1]); assertEq(doc2.lastModified, block.timestamp); @@ -538,19 +415,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentEngine.batchSetDocuments(smartContracts, names, uris, hashes); // Check the first document - IERC1643.Document memory doc1 = documentEngine.getDocument( - testContract, - documentName - ); + IERC1643.Document memory doc1 = documentEngine.getDocument(testContract, documentName); assertEq(doc1.uri, documentURI); assertEq(doc1.documentHash, documentHash); assertEq(doc1.lastModified, block.timestamp); // Check the second document - IERC1643.Document memory doc2 = documentEngine.getDocument( - testContract, - names[1] - ); + IERC1643.Document memory doc2 = documentEngine.getDocument(testContract, names[1]); assertEq(doc2.uri, uris[1]); assertEq(doc2.documentHash, hashes[1]); assertEq(doc2.lastModified, block.timestamp); @@ -620,10 +491,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { // Check that both documents are removed // Check the second document - IERC1643.Document memory doc = documentEngine.getDocument( - testContract, - documentName - ); + IERC1643.Document memory doc = documentEngine.getDocument(testContract, documentName); assertEq(doc.uri, ""); assertEq(doc.documentHash, ""); assertEq(doc.lastModified, 0); @@ -649,20 +517,14 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { // Check that both documents are removed // Check the second document - IERC1643.Document memory doc = documentEngine.getDocument( - testContract, - documentName - ); + IERC1643.Document memory doc = documentEngine.getDocument(testContract, documentName); assertEq(doc.uri, ""); assertEq(doc.documentHash, ""); assertEq(doc.lastModified, 0); bytes32[] memory docs = documentEngine.getAllDocuments(testContract); assertEq(docs.length, 0); - IERC1643.Document memory doc2 = documentEngine.getDocument( - anotherSmartContract, - names[1] - ); + IERC1643.Document memory doc2 = documentEngine.getDocument(anotherSmartContract, names[1]); assertEq(doc2.uri, ""); assertEq(doc2.documentHash, ""); assertEq(doc2.lastModified, 0); @@ -711,19 +573,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentEngine.batchSetDocuments(testContract, names, uris, hashes); // Check the first document - IERC1643.Document memory doc1 = documentEngine.getDocument( - testContract, - documentName - ); + IERC1643.Document memory doc1 = documentEngine.getDocument(testContract, documentName); assertEq(doc1.uri, documentURI); assertEq(doc1.documentHash, documentHash); assertEq(doc1.lastModified, block.timestamp); // Check the second document - IERC1643.Document memory doc2 = documentEngine.getDocument( - testContract, - names[1] - ); + IERC1643.Document memory doc2 = documentEngine.getDocument(testContract, names[1]); assertEq(doc2.uri, uris[1]); assertEq(doc2.documentHash, hashes[1]); assertEq(doc2.lastModified, block.timestamp); @@ -743,27 +599,19 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { // Check that both documents are removed // Check the second document - IERC1643.Document memory doc = documentEngine.getDocument( - testContract, - documentName - ); + IERC1643.Document memory doc = documentEngine.getDocument(testContract, documentName); assertEq(doc.uri, ""); assertEq(doc.documentHash, ""); assertEq(doc.lastModified, 0); bytes32[] memory docs = documentEngine.getAllDocuments(testContract); assertEq(docs.length, 0); - IERC1643.Document memory doc2 = documentEngine.getDocument( - testContract, - names[1] - ); + IERC1643.Document memory doc2 = documentEngine.getDocument(testContract, names[1]); assertEq(doc2.uri, ""); assertEq(doc2.documentHash, ""); assertEq(doc2.lastModified, 0); } - function testCannotRemoveBatchDocumentIfEmptyLengthForOnlyOneContract() - public - { + function testCannotRemoveBatchDocumentIfEmptyLengthForOnlyOneContract() public { bytes32[] memory names = new bytes32[](0); vm.expectRevert(abi.encodeWithSelector(InvalidInputLength.selector)); diff --git a/test/DocumentEngineOwnable.t.sol b/test/DocumentEngineOwnable.t.sol index ae6ea06..9e2caa8 100644 --- a/test/DocumentEngineOwnable.t.sol +++ b/test/DocumentEngineOwnable.t.sol @@ -34,9 +34,7 @@ contract DocumentEngineOwnableTest is Test { } function testDeployRevertsWithZeroOwner() public { - vm.expectRevert( - abi.encodeWithSelector(Ownable.OwnableInvalidOwner.selector, AddressZero) - ); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableInvalidOwner.selector, AddressZero)); new DocumentEngineOwnable(AddressZero, AddressZero); } @@ -46,10 +44,7 @@ contract DocumentEngineOwnableTest is Test { vm.prank(owner); engine.setDocument(testContract, documentName, documentURI, documentHash); - IERC1643.Document memory doc = engine.getDocument( - testContract, - documentName - ); + IERC1643.Document memory doc = engine.getDocument(testContract, documentName); assertEq(doc.uri, documentURI); assertEq(doc.documentHash, documentHash); assertEq(doc.lastModified, block.timestamp); @@ -62,12 +57,7 @@ contract DocumentEngineOwnableTest is Test { function testNonOwnerCannotSetDocument() public { vm.prank(attacker); - vm.expectRevert( - abi.encodeWithSelector( - Ownable.OwnableUnauthorizedAccount.selector, - attacker - ) - ); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, attacker)); engine.setDocument(testContract, documentName, documentURI, documentHash); } @@ -81,12 +71,7 @@ contract DocumentEngineOwnableTest is Test { function testNonOwnerCannotBindToken() public { vm.prank(attacker); - vm.expectRevert( - abi.encodeWithSelector( - Ownable.OwnableUnauthorizedAccount.selector, - attacker - ) - ); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, attacker)); engine.bindToken(testContract); } @@ -97,10 +82,7 @@ contract DocumentEngineOwnableTest is Test { vm.prank(testContract); engine.setDocument(documentName, documentURI, documentHash); - IERC1643.Document memory doc = engine.getDocument( - testContract, - documentName - ); + IERC1643.Document memory doc = engine.getDocument(testContract, documentName); assertEq(doc.uri, documentURI); vm.prank(testContract); @@ -120,23 +102,13 @@ contract DocumentEngineOwnableTest is Test { // once unbound, the token can no longer self-manage vm.prank(testContract); - vm.expectRevert( - abi.encodeWithSelector( - TokenBindingModule.NotBoundToken.selector, - testContract - ) - ); + vm.expectRevert(abi.encodeWithSelector(TokenBindingModule.NotBoundToken.selector, testContract)); engine.setDocument(documentName, documentURI, documentHash); } function testUnboundTokenCannotSelfManage() public { vm.prank(attacker); - vm.expectRevert( - abi.encodeWithSelector( - TokenBindingModule.NotBoundToken.selector, - attacker - ) - ); + vm.expectRevert(abi.encodeWithSelector(TokenBindingModule.NotBoundToken.selector, attacker)); engine.setDocument(documentName, documentURI, documentHash); } @@ -162,9 +134,7 @@ contract DocumentEngineOwnableTest is Test { assertTrue(engine.supportsInterface(type(IERC8303).interfaceId)); assertTrue(engine.supportsInterface(type(IERC165).interfaceId)); assertTrue(engine.supportsInterface(type(IERC1643).interfaceId)); - assertTrue( - engine.supportsInterface(type(IERC1643MultiDocument).interfaceId) - ); + assertTrue(engine.supportsInterface(type(IERC1643MultiDocument).interfaceId)); assertTrue(engine.supportsInterface(type(ITokenBinding).interfaceId)); // no role-based access control here assertFalse(engine.supportsInterface(type(IAccessControl).interfaceId)); From 1432af94fe0b9dffae20034a1e22e64c349f9f4e Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 18:41:49 +0200 Subject: [PATCH 17/47] perf: cache batch-loop length and use ++i in DocumentEngineBase --- src/DocumentEngineBase.sol | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/DocumentEngineBase.sol b/src/DocumentEngineBase.sol index f104daf..90286cf 100644 --- a/src/DocumentEngineBase.sol +++ b/src/DocumentEngineBase.sol @@ -120,7 +120,8 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen ) { revert InvalidInputLength(); } - for (uint256 i = 0; i < subjects.length; i++) { + uint256 length = subjects.length; + for (uint256 i = 0; i < length; ++i) { _setDocument(subjects[i], names[i], uris[i], hashes[i]); } } @@ -137,7 +138,8 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen if (names.length == 0 || names.length != uris.length || uris.length != hashes.length) { revert InvalidInputLength(); } - for (uint256 i = 0; i < names.length; ++i) { + uint256 length = names.length; + for (uint256 i = 0; i < length; ++i) { _setDocument(subject, names[i], uris[i], hashes[i]); } } @@ -150,7 +152,8 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen revert InvalidInputLength(); } - for (uint256 i = 0; i < subjects.length; ++i) { + uint256 length = subjects.length; + for (uint256 i = 0; i < length; ++i) { _removeDocument(subjects[i], names[i]); } } @@ -163,7 +166,8 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen revert InvalidInputLength(); } - for (uint256 i = 0; i < names.length; ++i) { + uint256 length = names.length; + for (uint256 i = 0; i < length; ++i) { _removeDocument(subject, names[i]); } } From 5b9634748f0fb93799d44eb4b62828a39c9f7fef Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 18:52:19 +0200 Subject: [PATCH 18/47] test: add event-emission, msg.sender-read, batch-guard, and fuzz coverage --- AGENTS.md | 10 ++- CLAUDE.md | 10 ++- test/DocumentEngine.t.sol | 177 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+), 8 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 6a86b3d..9688bd1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -92,10 +92,12 @@ script/ └── DeployDocumentEngineOwnable.s.sol # Deploy Ownable variant (env: DOCUMENT_ENGINE_OWNER, _FORWARDER) test/ -├── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin path, -│ # bound-token path, batch ops, ERC-8303, CMTAT integration -│ # (CMTATDocumentEngineMock built on DocumentEngineModule), -│ # flexible-authorization override (OpenDocumentEngine) +├── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin + bound-token +│ # paths, batch ops (incl. name==0 / missing-doc guards), +│ # ERC-8303 + interface discovery, event emission (asserts the +│ # base events are NOT emitted), msg.sender-scoped reads, +│ # enumeration, fuzz round-trip/isolation, CMTAT integration +│ # (CMTATDocumentEngineMock), flexible-auth override (OpenDocumentEngine) ├── DocumentEngineOwnable.t.sol # Tests for the Ownable2Step deployment (owner path, │ # token binding, two-step ownership, ERC-8303) └── Deploy.t.sol # Tests for the deployment scripts (deploy() state + run() env) diff --git a/CLAUDE.md b/CLAUDE.md index 6a86b3d..9688bd1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -92,10 +92,12 @@ script/ └── DeployDocumentEngineOwnable.s.sol # Deploy Ownable variant (env: DOCUMENT_ENGINE_OWNER, _FORWARDER) test/ -├── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin path, -│ # bound-token path, batch ops, ERC-8303, CMTAT integration -│ # (CMTATDocumentEngineMock built on DocumentEngineModule), -│ # flexible-authorization override (OpenDocumentEngine) +├── DocumentEngine.t.sol # Foundry tests: deploy, access control, admin + bound-token +│ # paths, batch ops (incl. name==0 / missing-doc guards), +│ # ERC-8303 + interface discovery, event emission (asserts the +│ # base events are NOT emitted), msg.sender-scoped reads, +│ # enumeration, fuzz round-trip/isolation, CMTAT integration +│ # (CMTATDocumentEngineMock), flexible-auth override (OpenDocumentEngine) ├── DocumentEngineOwnable.t.sol # Tests for the Ownable2Step deployment (owner path, │ # token binding, two-step ownership, ERC-8303) └── Deploy.t.sol # Tests for the deployment scripts (deploy() state + run() env) diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index b05af5b..9d17f3a 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -55,6 +55,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { bytes32 public constant DOCUMENT_MANAGER_ROLE = keccak256("DOCUMENT_MANAGER_ROLE"); address AddressZero = address(0); + // Local copies of the extension events, so `vm.expectEmit` can emit and match them. + event DocumentUpdatedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); + event DocumentRemovedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); + // Base ERC-1643 event signatures (this shared engine must NOT emit them). + bytes32 internal constant BASE_UPDATED_SIG = keccak256("DocumentUpdated(bytes32,string,bytes32)"); + bytes32 internal constant BASE_REMOVED_SIG = keccak256("DocumentRemoved(bytes32,string,bytes32)"); + function setUp() public { documentEngine = new DocumentEngine(admin, AddressZero); vm.prank(admin); @@ -618,4 +625,174 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { vm.prank(admin); documentEngine.batchRemoveDocuments(testContract, names); } + + /*////////////////////////////////////////////////////////////// + Events (emission responsibility) + //////////////////////////////////////////////////////////////*/ + + function testSetDocumentEmitsForSubjectEvent() public { + bytes32 name = keccak256("evt-doc"); + vm.expectEmit(true, true, false, true, address(documentEngine)); + emit DocumentUpdatedForSubject(testContract, name, documentURI, documentHash); + vm.prank(admin); + documentEngine.setDocument(testContract, name, documentURI, documentHash); + } + + function testRemoveDocumentEmitsForSubjectEvent() public { + // `documentName` for `testContract` was registered in setUp + vm.expectEmit(true, true, false, true, address(documentEngine)); + emit DocumentRemovedForSubject(testContract, documentName, documentURI, documentHash); + vm.prank(admin); + documentEngine.removeDocument(testContract, documentName); + } + + function testSetDocumentDoesNotEmitBaseEvent() public { + vm.recordLogs(); + vm.prank(admin); + documentEngine.setDocument(testContract, keccak256("evt-doc"), documentURI, documentHash); + _assertBaseEventNotEmitted(BASE_UPDATED_SIG); + } + + function testRemoveDocumentDoesNotEmitBaseEvent() public { + vm.recordLogs(); + vm.prank(admin); + documentEngine.removeDocument(testContract, documentName); + _assertBaseEventNotEmitted(BASE_REMOVED_SIG); + } + + /// @dev Asserts no recorded log emitted by the engine carries the base ERC-1643 signature. + function _assertBaseEventNotEmitted(bytes32 baseSig) internal { + Vm.Log[] memory logs = vm.getRecordedLogs(); + for (uint256 i = 0; i < logs.length; ++i) { + if (logs[i].emitter == address(documentEngine)) { + assertTrue(logs[i].topics[0] != baseSig, "base ERC-1643 event must not be emitted"); + } + } + } + + /*////////////////////////////////////////////////////////////// + msg.sender-scoped reads (base ERC-1643) + //////////////////////////////////////////////////////////////*/ + + function testMsgSenderScopedReads() public { + // setUp registered `documentName` for `testContract`; read it as that caller + vm.prank(testContract); + IERC1643.Document memory doc = documentEngine.getDocument(documentName); + assertEq(doc.uri, documentURI); + assertEq(doc.documentHash, documentHash); + + vm.prank(testContract); + bytes32[] memory names = documentEngine.getAllDocuments(); + assertEq(names.length, 1); + assertEq(names[0], documentName); + } + + function testMsgSenderScopedReadReturnsEmptyForOther() public { + // `attacker` has no documents of its own + vm.prank(attacker); + IERC1643.Document memory doc = documentEngine.getDocument(documentName); + assertEq(doc.uri, ""); + assertEq(doc.documentHash, ""); + assertEq(doc.lastModified, 0); + + vm.prank(attacker); + assertEq(documentEngine.getAllDocuments().length, 0); + } + + /*////////////////////////////////////////////////////////////// + Batch edge cases (name==0 / missing doc) + //////////////////////////////////////////////////////////////*/ + + function testBatchSetRevertsOnZeroName() public { + address[] memory subjects = new address[](1); + subjects[0] = testContract; + bytes32[] memory names = new bytes32[](1); + names[0] = bytes32(0); + string[] memory uris = new string[](1); + uris[0] = documentURI; + bytes32[] memory hashes = new bytes32[](1); + hashes[0] = documentHash; + + vm.prank(admin); + vm.expectRevert(abi.encodeWithSelector(ERC1643InvalidName.selector)); + documentEngine.batchSetDocuments(subjects, names, uris, hashes); + } + + function testBatchRemoveRevertsOnMissingDocument() public { + address[] memory subjects = new address[](1); + subjects[0] = testContract; + bytes32[] memory names = new bytes32[](1); + names[0] = keccak256("never-set"); + + vm.prank(admin); + vm.expectRevert(abi.encodeWithSelector(ERC1643MissingDocument.selector)); + documentEngine.batchRemoveDocuments(subjects, names); + } + + /*////////////////////////////////////////////////////////////// + Enumeration & fuzz + //////////////////////////////////////////////////////////////*/ + + function testEnumerationAfterMixedOps() public { + address subj = address(0xBEEF); + bytes32 n1 = keccak256("n1"); + bytes32 n2 = keccak256("n2"); + bytes32 n3 = keccak256("n3"); + + vm.startPrank(admin); + documentEngine.setDocument(subj, n1, "u1", bytes32(0)); + documentEngine.setDocument(subj, n2, "u2", bytes32(0)); + documentEngine.setDocument(subj, n3, "u3", bytes32(0)); + assertEq(documentEngine.getAllDocuments(subj).length, 3); + + // overwrite does not add a new entry + documentEngine.setDocument(subj, n2, "u2-updated", bytes32(0)); + assertEq(documentEngine.getAllDocuments(subj).length, 3); + + // removal shrinks the set (swap-and-pop) + documentEngine.removeDocument(subj, n2); + vm.stopPrank(); + + bytes32[] memory names = documentEngine.getAllDocuments(subj); + assertEq(names.length, 2); + assertTrue( + (names[0] == n1 && names[1] == n3) || (names[0] == n3 && names[1] == n1), + "remaining names must be n1 and n3" + ); + } + + function testFuzzSetGetRemoveRoundTrip(address subject, bytes32 name, string calldata uri, bytes32 hash) public { + vm.assume(name != bytes32(0)); // the null name reverts by design + + vm.prank(admin); + documentEngine.setDocument(subject, name, uri, hash); + + IERC1643.Document memory doc = documentEngine.getDocument(subject, name); + assertEq(doc.uri, uri); + assertEq(doc.documentHash, hash); + assertEq(doc.lastModified, block.timestamp); + + vm.prank(admin); + documentEngine.removeDocument(subject, name); + + doc = documentEngine.getDocument(subject, name); + assertEq(doc.uri, ""); + assertEq(doc.documentHash, ""); + assertEq(doc.lastModified, 0); + } + + function testFuzzDocumentsAreIsolatedPerSubject(address subjectA, address subjectB, bytes32 name) public { + vm.assume(name != bytes32(0)); + vm.assume(subjectA != subjectB); + // `testContract` is pre-populated in setUp; exclude it from the "untouched" subject + vm.assume(subjectB != testContract); + + vm.prank(admin); + documentEngine.setDocument(subjectA, name, documentURI, documentHash); + + // subjectB is unaffected + IERC1643.Document memory docB = documentEngine.getDocument(subjectB, name); + assertEq(docB.lastModified, 0); + assertEq(documentEngine.getAllDocuments(subjectB).length, 0); + } } From b26b74f836bec1fc24565251f05a7d6804a25814 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 19:01:49 +0200 Subject: [PATCH 19/47] refactor: remove unused DocumentNotFound error --- src/DocumentEngineInvariant.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/DocumentEngineInvariant.sol b/src/DocumentEngineInvariant.sol index 93bfb37..3428b6c 100644 --- a/src/DocumentEngineInvariant.sol +++ b/src/DocumentEngineInvariant.sol @@ -11,7 +11,6 @@ pragma solidity ^0.8.20; * contract is only ever used as a base, never deployed on its own. */ abstract contract DocumentEngineInvariant { - error DocumentNotFound(address subject, bytes32 name); error InvalidInputLength(); error AdminWithAddressZeroNotAllowed(); From 39fd5c959221df8a2aaccaf099f0ec496a55c335 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Wed, 22 Jul 2026 19:21:39 +0200 Subject: [PATCH 20/47] feat: reject null subject in multi-token setDocument (ERC1643InvalidSubject) --- AGENTS.md | 3 ++- CHANGELOG.md | 2 +- src/DocumentEngineBase.sol | 5 +++++ src/DocumentEngineInvariant.sol | 4 ++++ test/DocumentEngine.t.sol | 23 +++++++++++++++++++++++ 5 files changed, 35 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 9688bd1..ba90d70 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -73,7 +73,8 @@ src/ ├── DocumentEngineOwnable.sol # Deployment #2: Ownable2Step (single owner) instead of │ # roles; document mgmt + binding are owner-only ├── DocumentEngineInvariant.sol # Shared errors only (incl. ERC1643InvalidName / -│ # ERC1643MissingDocument); NO access-control specifics +│ # ERC1643InvalidSubject / ERC1643MissingDocument); +│ # NO access-control specifics ├── interfaces/ │ ├── IERC8303.sol # ERC-8303 "Contract Version" interface (id 0x54fd4d50) │ ├── IERC1643MultiDocument.sol # Multi-token ERC-1643 extension (address-scoped fns + diff --git a/CHANGELOG.md b/CHANGELOG.md index a6266bc..fb0ddad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,7 +75,7 @@ Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/er - **Emission responsibility.** As a shared, multi-token manager the engine now emits **only** the address-carrying extension events and **no longer** emits the base `DocumentUpdated` / `DocumentRemoved` events (the spec's `MUST NOT` for a shared manager — those events carry no `subject` and belong on the token contract). - **Extension events/interface.** Renamed the multi-token events to the standard `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` (parameter `subject`), and introduced the `IERC1643MultiDocument` interface (`src/interfaces/IERC1643MultiDocument.sol`) that the base now implements — the address-scoped `getDocument` / `getAllDocuments` / `setDocument` / `removeDocument`. -- **Input validation.** `setDocument` now reverts `ERC1643InvalidName()` when `name == bytes32(0)`; `removeDocument` now reverts `ERC1643MissingDocument()` for a non-existent document (previously it silently emitted a spurious removal event). +- **Input validation.** `setDocument` now reverts `ERC1643InvalidName()` when `name == bytes32(0)` and `ERC1643InvalidSubject()` when `subject == address(0)` (the multi-token extension's null-namespace guard); `removeDocument` now reverts `ERC1643MissingDocument()` for a non-existent document (previously it silently emitted a spurious removal event). See [`IMP.md`](./IMP.md) for the proposed corresponding ERC-1643 extension note. - **ERC-165 discovery.** `supportsInterface` now returns `true` for `type(IERC1643).interfaceId` and `type(IERC1643MultiDocument).interfaceId` (both deployments). ### Added (token binding) diff --git a/src/DocumentEngineBase.sol b/src/DocumentEngineBase.sol index 90286cf..61f49a1 100644 --- a/src/DocumentEngineBase.sol +++ b/src/DocumentEngineBase.sol @@ -243,6 +243,11 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen } function _setDocument(address subject, bytes32 name_, string memory uri_, bytes32 documentHash_) internal { + // Multi-token guard: `subject` must be a real contract address, never the + // null namespace. (The bound-token path passes `_msgSender()`, never zero.) + if (subject == address(0)) { + revert ERC1643InvalidSubject(); + } // ERC-1643: reject the null name (ambiguous / default key) if (name_ == bytes32(0)) { revert ERC1643InvalidName(); diff --git a/src/DocumentEngineInvariant.sol b/src/DocumentEngineInvariant.sol index 3428b6c..d54d107 100644 --- a/src/DocumentEngineInvariant.sol +++ b/src/DocumentEngineInvariant.sol @@ -18,6 +18,10 @@ abstract contract DocumentEngineInvariant { /// @dev ERC-1643-recommended error name. error ERC1643InvalidName(); + /// @notice Reverts when a document is set for the null `subject` (`address(0)`). + /// @dev Multi-token extension guard; see IMP.md for the proposed standard note. + error ERC1643InvalidSubject(); + /// @notice Reverts when `removeDocument` targets a document that does not exist. /// @dev ERC-1643-recommended error name. error ERC1643MissingDocument(); diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index 9d17f3a..8a089af 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -703,6 +703,27 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { Batch edge cases (name==0 / missing doc) //////////////////////////////////////////////////////////////*/ + function testCannotSetDocumentForZeroSubject() public { + vm.prank(admin); + vm.expectRevert(abi.encodeWithSelector(ERC1643InvalidSubject.selector)); + documentEngine.setDocument(AddressZero, documentName, documentURI, documentHash); + } + + function testBatchSetRevertsOnZeroSubject() public { + address[] memory subjects = new address[](1); + subjects[0] = AddressZero; + bytes32[] memory names = new bytes32[](1); + names[0] = documentName; + string[] memory uris = new string[](1); + uris[0] = documentURI; + bytes32[] memory hashes = new bytes32[](1); + hashes[0] = documentHash; + + vm.prank(admin); + vm.expectRevert(abi.encodeWithSelector(ERC1643InvalidSubject.selector)); + documentEngine.batchSetDocuments(subjects, names, uris, hashes); + } + function testBatchSetRevertsOnZeroName() public { address[] memory subjects = new address[](1); subjects[0] = testContract; @@ -763,6 +784,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { function testFuzzSetGetRemoveRoundTrip(address subject, bytes32 name, string calldata uri, bytes32 hash) public { vm.assume(name != bytes32(0)); // the null name reverts by design + vm.assume(subject != AddressZero); // the null subject reverts by design vm.prank(admin); documentEngine.setDocument(subject, name, uri, hash); @@ -784,6 +806,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { function testFuzzDocumentsAreIsolatedPerSubject(address subjectA, address subjectB, bytes32 name) public { vm.assume(name != bytes32(0)); vm.assume(subjectA != subjectB); + vm.assume(subjectA != AddressZero); // the null subject reverts by design // `testContract` is pre-populated in setUp; exclude it from the "untouched" subject vm.assume(subjectB != testContract); From 5d13ee03d756ac0780cb1a0addee3e731557866e Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Tue, 28 Jul 2026 15:50:31 +0200 Subject: [PATCH 21/47] chore: upgrade to CMTAT v3.3.0-rc2, add version compatibility matrix, record aderyn v0.4.0 run --- AGENTS.md | 26 +- CHANGELOG.md | 48 ++- CLAUDE.md | 25 +- README.md | 74 +++- doc/ERCSpecification/erc-1643.md | 124 +++---- .../erc-draft_multi_document_management.md | 268 +++++++++++++++ doc/audits/AUDIT_OVERVIEW.md | 66 ++++ .../v0.4.0/aderyn/aderyn-report-feedback.md | 49 +++ .../tools/v0.4.0/aderyn/aderyn-report.md | 323 ++++++++++++++++++ foundry.lock | 26 +- src/DocumentEngineBase.sol | 29 +- src/DocumentEngineInvariant.sol | 15 +- src/interfaces/IERC1643MultiDocument.sol | 14 +- test/DocumentEngine.t.sol | 114 +++++-- test/DocumentEngineOwnable.t.sol | 21 +- 15 files changed, 1062 insertions(+), 160 deletions(-) create mode 100644 doc/ERCSpecification/erc-draft_multi_document_management.md create mode 100644 doc/audits/AUDIT_OVERVIEW.md create mode 100644 doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md create mode 100644 doc/audits/tools/v0.4.0/aderyn/aderyn-report.md diff --git a/AGENTS.md b/AGENTS.md index ba90d70..97f4dd0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -43,6 +43,15 @@ addressed by a `bytes32` name. - **ERC-1643 conformance:** `setDocument` reverts `ERC1643InvalidName()` on `name == 0`; `removeDocument` reverts `ERC1643MissingDocument()` on a missing doc; `supportsInterface` advertises `IERC1643` + `IERC1643MultiDocument` (both deployments). + Both errors are declared by `IERC1643` itself since CMTAT `v3.3.0-rc2` — do **not** + re-declare them in `DocumentEngineInvariant` (duplicate declaration = compile error, + and the multi-subject draft forbids it). +- **`getDocument` returns flat values**, `(string uri, bytes32 documentHash, + uint256 lastModified)`, never the `Document` struct — the struct is storage-only. + A struct return prepends an offset word to the returndata while leaving the selector + and `type(IERC1643).interfaceId` unchanged, so the mismatch is invisible to ERC-165 + and a spec-conformant consumer silently mis-decodes. Pinned by + `testGetDocumentReturnsFlatErc1643Abi`; see `ERC_RESULT.md` §4.1. - **ERC-2771:** meta-transaction (gasless) support; `_msgSender()` is used everywhere. - **Access control:** `DEFAULT_ADMIN_ROLE` implicitly has every role (see the `hasRole` override). @@ -72,9 +81,10 @@ src/ │ # supportsInterface, constructor ├── DocumentEngineOwnable.sol # Deployment #2: Ownable2Step (single owner) instead of │ # roles; document mgmt + binding are owner-only -├── DocumentEngineInvariant.sol # Shared errors only (incl. ERC1643InvalidName / -│ # ERC1643InvalidSubject / ERC1643MissingDocument); -│ # NO access-control specifics +├── DocumentEngineInvariant.sol # Shared errors only (InvalidInputLength, +│ # AdminWithAddressZeroNotAllowed, ERC1643InvalidSubject); +│ # ERC1643InvalidName / ERC1643MissingDocument come from +│ # IERC1643. NO access-control specifics ├── interfaces/ │ ├── IERC8303.sol # ERC-8303 "Contract Version" interface (id 0x54fd4d50) │ ├── IERC1643MultiDocument.sol # Multi-token ERC-1643 extension (address-scoped fns + @@ -118,12 +128,18 @@ Other important files: - `CHANGELOG.md` — semver history; update on every release (current: `v0.4.0`). - `ERC-1643-proposition.md` — proposed optional multi-token events / extension. - `README.md` — full documentation and Surya schema. -- `doc/` — Surya diagrams/reports (`doc/script/`), Slither report, coverage. +- `doc/` — Surya diagrams/reports (`doc/script/`), coverage, and + `doc/audits/` — the security overview (`AUDIT_OVERVIEW.md`) plus versioned + static-analysis output under `doc/audits/tools/vX.Y.Z//`, each with a + `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging + every finding. Aderyn was run for `v0.4.0`; Slither has never been run here. +- `ERC_RESULT.md` — conformance analysis against both ERC specifications; the + open items live in its §7. - `lib/` — submodules: `CMTAT`, `RuleEngine`, `openzeppelin-contracts(-upgradeable)`, `forge-std`. ## Dependencies (tested versions) -- CMTAT `v3.3.0-rc1`, RuleEngine `v3.0.0-rc4` (binding-pattern reference only; compliance module not reused) +- CMTAT `v3.3.0-rc2`, RuleEngine `v3.0.0-rc4` (binding-pattern reference only; compliance module not reused) - OpenZeppelin Contracts / Contracts Upgradeable `v5.6.1` - Solidity `0.8.34`, Foundry diff --git a/CHANGELOG.md b/CHANGELOG.md index fb0ddad..e62c7f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,16 +43,45 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) ## v0.4.0 +Targets **CMTAT `v3.3.0-rc2`** — see the [compatibility matrix](./README.md#version-compatibility) +for which CMTAT release each version of this engine is built against. + +> **Versioning note.** `getDocument` changes shape relative to `v0.3.0`, which the convention above +> classifies as a MAJOR bump. `MINOR` is used because the project is still in its `0.x` line, where a +> `1.0.0` would wrongly signal a stable, audited release. Treat this release as breaking for any +> consumer decoding `getDocument`. + ### Changed - **Dependencies** - - Upgrade CMTAT `v2.5.0-rc0` → `v3.3.0-rc1` + - Upgrade CMTAT `v2.5.0-rc0` → [`v3.3.0-rc2`](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc2) + (`lib/CMTAT` → `35d8940b40943828c5ea407dc6b22d559d92e4ae`). Development passed through + `v3.3.0-rc1`; that interim release is **not** compatible with the code as shipped here, because + it declares neither the ERC-1643 errors nor the flat `getDocument` return (see below). - Upgrade OpenZeppelin Contracts (and Contracts Upgradeable) `v5.0.2` → `v5.6.1` - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) `v3.0.0-rc4` as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) + - `foundry.lock` now records every submodule by tag; all five entries had gone stale since `v0.3.0`. - **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) - **`IERC1643` (CMTAT v3) breaking changes** - - `getDocument(bytes32)` now returns a `Document` struct instead of the `(string, bytes32, uint256)` tuple. Both `getDocument` overloads updated accordingly. - - The `Document` struct and the `DocumentUpdated`/`DocumentRemoved` events are now provided by `IERC1643`; the duplicate local declarations were removed from `DocumentEngineInvariant`. + - `getDocument` keeps returning `(string uri, bytes32 documentHash, uint256 lastModified)` — the + flat ERC-1643 ABI — on **both** overloads, `getDocument(bytes32)` and + `getDocument(address subject, bytes32)`. CMTAT `v3.3.0-rc1` briefly replaced this with a + `Document` struct and `v3.3.0-rc2` reverted it; this engine follows rc2, so relative to `v0.3.0` + the external shape is unchanged. + + The distinction is worth recording because it is invisible to interface detection: return types + are not part of a function signature, so both shapes share the same selectors and the same + `type(IERC1643).interfaceId` (`0xecfecec8`). A consumer built from the specification ABI decodes + a struct return as garbage *without reverting* — `uri` becomes binary junk, `documentHash` + becomes `0x…60`, and `lastModified` becomes the real hash as a `uint256`. `getDocument` is now + covered by `testGetDocumentReturnsFlatErc1643Abi`, which inspects the returndata directly since + ERC-165 structurally cannot. + - The `Document` struct and the `DocumentUpdated`/`DocumentRemoved` events are now provided by `IERC1643`; the duplicate local declarations were removed from `DocumentEngineInvariant`. The struct is retained internally for storage only. + - `ERC1643InvalidName()` / `ERC1643MissingDocument()` are likewise declared by `IERC1643` as of + CMTAT `v3.3.0-rc2` and are **not** re-declared here. The multi-subject draft requires a contract + implementing both interfaces to obtain each error exactly once ("MUST NOT declare them twice"), + and re-declaring is a compile error. Selectors, and hence revert data, are unchanged. + `ERC1643InvalidSubject()` stays local, since no interface defines it. - Import path moved: `CMTAT/interfaces/engine/draft-IERC1643.sol` → `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. ### Added @@ -75,7 +104,7 @@ Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/er - **Emission responsibility.** As a shared, multi-token manager the engine now emits **only** the address-carrying extension events and **no longer** emits the base `DocumentUpdated` / `DocumentRemoved` events (the spec's `MUST NOT` for a shared manager — those events carry no `subject` and belong on the token contract). - **Extension events/interface.** Renamed the multi-token events to the standard `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` (parameter `subject`), and introduced the `IERC1643MultiDocument` interface (`src/interfaces/IERC1643MultiDocument.sol`) that the base now implements — the address-scoped `getDocument` / `getAllDocuments` / `setDocument` / `removeDocument`. -- **Input validation.** `setDocument` now reverts `ERC1643InvalidName()` when `name == bytes32(0)` and `ERC1643InvalidSubject()` when `subject == address(0)` (the multi-token extension's null-namespace guard); `removeDocument` now reverts `ERC1643MissingDocument()` for a non-existent document (previously it silently emitted a spurious removal event). See [`IMP.md`](./IMP.md) for the proposed corresponding ERC-1643 extension note. +- **Input validation.** `setDocument` now reverts `ERC1643InvalidName()` when `name == bytes32(0)` and `ERC1643InvalidSubject()` when `subject == address(0)` (the multi-token extension's null-namespace guard); `removeDocument` now reverts `ERC1643MissingDocument()` for a non-existent document (previously it silently emitted a spurious removal event). See [`erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md) for the corresponding multi-subject draft. - **ERC-165 discovery.** `supportsInterface` now returns `true` for `type(IERC1643).interfaceId` and `type(IERC1643MultiDocument).interfaceId` (both deployments). ### Added (token binding) @@ -84,6 +113,17 @@ Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/er ### Notes / bottlenecks +- **Subject-side emission is CMTAT `v3.3.0-rc2` or later.** rc2 made `DocumentEngineModule` re-emit + the standard `DocumentUpdated` / `DocumentRemoved` on the **token's own address** after forwarding + to the engine, and revert with `CMTAT_DocumentEngineModule_NoDocumentEngine` when no engine is set. + Combined with this engine emitting only the address-carrying `*ForSubject` events, the + subject-initiated call topology is fully conformant with the multi-subject draft's *Emission + Responsibility* rules. The **admin path remains non-conformant by construction** — a write sent + straight to the engine has no execution point in the subject, so the subject emits nothing. + See [`ERC_RESULT.md`](./ERC_RESULT.md) §4.3. +- Open conformance gaps are tracked in [`ERC_RESULT.md`](./ERC_RESULT.md): per-`subject` + authorization (§4.2), the `ERC1643InvalidSubject` / `MultiDocumentInvalidSubject` naming + divergence (§4.4), and enumeration cost (§4.7). - CMTAT v3 no longer ships a *standalone* token that consumes an external document engine through its constructor; the standard token stores documents on-chain (`DocumentERC1643Module`). External-engine integration now goes through CMTAT's `DocumentEngineModule` (`setDocumentEngine`). The test suite was updated to exercise this real integration path via a minimal token built on `DocumentEngineModule`. ## v0.3.0 diff --git a/CLAUDE.md b/CLAUDE.md index 9688bd1..97f4dd0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -43,6 +43,15 @@ addressed by a `bytes32` name. - **ERC-1643 conformance:** `setDocument` reverts `ERC1643InvalidName()` on `name == 0`; `removeDocument` reverts `ERC1643MissingDocument()` on a missing doc; `supportsInterface` advertises `IERC1643` + `IERC1643MultiDocument` (both deployments). + Both errors are declared by `IERC1643` itself since CMTAT `v3.3.0-rc2` — do **not** + re-declare them in `DocumentEngineInvariant` (duplicate declaration = compile error, + and the multi-subject draft forbids it). +- **`getDocument` returns flat values**, `(string uri, bytes32 documentHash, + uint256 lastModified)`, never the `Document` struct — the struct is storage-only. + A struct return prepends an offset word to the returndata while leaving the selector + and `type(IERC1643).interfaceId` unchanged, so the mismatch is invisible to ERC-165 + and a spec-conformant consumer silently mis-decodes. Pinned by + `testGetDocumentReturnsFlatErc1643Abi`; see `ERC_RESULT.md` §4.1. - **ERC-2771:** meta-transaction (gasless) support; `_msgSender()` is used everywhere. - **Access control:** `DEFAULT_ADMIN_ROLE` implicitly has every role (see the `hasRole` override). @@ -72,8 +81,10 @@ src/ │ # supportsInterface, constructor ├── DocumentEngineOwnable.sol # Deployment #2: Ownable2Step (single owner) instead of │ # roles; document mgmt + binding are owner-only -├── DocumentEngineInvariant.sol # Shared errors only (incl. ERC1643InvalidName / -│ # ERC1643MissingDocument); NO access-control specifics +├── DocumentEngineInvariant.sol # Shared errors only (InvalidInputLength, +│ # AdminWithAddressZeroNotAllowed, ERC1643InvalidSubject); +│ # ERC1643InvalidName / ERC1643MissingDocument come from +│ # IERC1643. NO access-control specifics ├── interfaces/ │ ├── IERC8303.sol # ERC-8303 "Contract Version" interface (id 0x54fd4d50) │ ├── IERC1643MultiDocument.sol # Multi-token ERC-1643 extension (address-scoped fns + @@ -117,12 +128,18 @@ Other important files: - `CHANGELOG.md` — semver history; update on every release (current: `v0.4.0`). - `ERC-1643-proposition.md` — proposed optional multi-token events / extension. - `README.md` — full documentation and Surya schema. -- `doc/` — Surya diagrams/reports (`doc/script/`), Slither report, coverage. +- `doc/` — Surya diagrams/reports (`doc/script/`), coverage, and + `doc/audits/` — the security overview (`AUDIT_OVERVIEW.md`) plus versioned + static-analysis output under `doc/audits/tools/vX.Y.Z//`, each with a + `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging + every finding. Aderyn was run for `v0.4.0`; Slither has never been run here. +- `ERC_RESULT.md` — conformance analysis against both ERC specifications; the + open items live in its §7. - `lib/` — submodules: `CMTAT`, `RuleEngine`, `openzeppelin-contracts(-upgradeable)`, `forge-std`. ## Dependencies (tested versions) -- CMTAT `v3.3.0-rc1`, RuleEngine `v3.0.0-rc4` (binding-pattern reference only; compliance module not reused) +- CMTAT `v3.3.0-rc2`, RuleEngine `v3.0.0-rc4` (binding-pattern reference only; compliance module not reused) - OpenZeppelin Contracts / Contracts Upgradeable `v5.6.1` - Solidity `0.8.34`, Foundry diff --git a/README.md b/README.md index 14801ce..968de1b 100644 --- a/README.md +++ b/README.md @@ -13,23 +13,31 @@ The ERC-1643 defines a document with three attributes: - A generic URI (represented as a `string`) that could point to a website or other document portal. - The hash of the document contents associated with it on-chain. -A smart contract needs only to read documents from this standard through the interface [IERC1643](./lib/CMTAT/contracts/interfaces/tokenization/draft-IERC1643.sol) to get the documents from the documentEngine. Since CMTAT v3, `getDocument` returns a `Document` struct: +A smart contract needs only to read documents from this standard through the interface [IERC1643](./lib/CMTAT/contracts/interfaces/tokenization/draft-IERC1643.sol) to get the documents from the documentEngine: ```solidity interface IERC1643 { - struct Document { - string uri; - bytes32 documentHash; - uint256 lastModified; - } + error ERC1643InvalidName(); + error ERC1643MissingDocument(); - function getDocument(bytes32 name) external view returns (Document memory document); + function getDocument(bytes32 name) + external + view + returns (string memory uri, bytes32 documentHash, uint256 lastModified); function getAllDocuments() external view returns (bytes32[] memory documentNames_); function setDocument(bytes32 name, string calldata uri, bytes32 documentHash) external; function removeDocument(bytes32 name) external; } ``` +> **Note — `getDocument` returns flat values.** CMTAT `v3.3.0-rc1` briefly returned a `Document` +> struct here; `v3.3.0-rc2` restored the three flat return values mandated by the ERC-1643 ABI, and +> this engine follows. The distinction matters because return types are not part of a function +> signature: both shapes have the same selector and the same `type(IERC1643).interfaceId`, so a +> struct return is undetectable through ERC-165 and a consumer built from the specification ABI +> would silently decode it as garbage. The `Document` struct is kept internally for storage only. +> `testGetDocumentReturnsFlatErc1643Abi` pins the wire format. + Using an external contract for your smart contract provides two advantages: - Reduce code size of your smart contract @@ -290,10 +298,37 @@ The toolchain includes the following components, where the versions are the late - Solidity 0.8.34 (via solc-js), `evm_version = prague` - OpenZeppelin Contracts (submodule) [v5.6.1](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.6.1) - Tests - - [CMTAT v3.3.0-rc1](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc1) + - [CMTAT v3.3.0-rc2](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc2) - [RuleEngine v3.0.0-rc4](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc4) (binding-pattern reference only — its compliance module is [not reused](#why-not-reuse-ruleengines-erc-3643-compliance-module)) - OpenZeppelin Contracts Upgradeable (submodule) [v5.6.1](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v5.6.1) +### Version compatibility + +Each release of this engine is built and tested against one CMTAT release. CMTAT's `IERC1643` is +not stable across its own release candidates, so pairing a version of this engine with a different +CMTAT than the one below is not supported. + +| DocumentEngine | CMTAT | Solidity / `evm_version` | OpenZeppelin | `getDocument` returns | +| -------------- | ----- | ------------------------ | ------------ | --------------------- | +| **v0.4.0** (current) | [v3.3.0-rc2](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc2) | `0.8.34` / `prague` | v5.6.1 | `(string, bytes32, uint256)` | +| v0.3.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | +| v0.2.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | +| v0.1.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | + +Notes on the CMTAT v2 → v3 jump at `v0.4.0`: + +- **CMTAT `v3.3.0-rc1` is not supported.** It is the one release in which `IERC1643.getDocument` + returns a `Document` struct rather than the three flat values; `v3.3.0-rc2` reverted that. rc1 also + does not declare `ERC1643InvalidName` / `ERC1643MissingDocument` on the interface. Building this + engine against rc1 fails to compile. +- The `IERC1643` import path moved in CMTAT v3, from + `CMTAT/interfaces/engine/draft-IERC1643.sol` to `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. +- Document names became `bytes32` in CMTAT v3 (they were `string` up to v2.5.0-rc0). +- Solidity `≥ 0.8.27` is required from `v0.4.0` on, because CMTAT v3 uses + `require(cond, CustomError())`. + +Exact submodule revisions are pinned in [`foundry.lock`](./foundry.lock). + ## Tools ### Formatting (forge fmt) @@ -306,12 +341,31 @@ forge fmt # format src/, test/, script/ forge fmt --check # verify formatting (CI) ``` -### Slither +### Static analysis + +Reports are versioned under [`doc/audits/tools/`](./doc/audits/tools), one directory per release, +each with the raw tool output (prefixed by a summary table) and a feedback file triaging every +finding against the source. The security overview is +[`doc/audits/AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md). + +| Release | Tool | Result | Report | Triage | +| ------- | ---- | ------ | ------ | ------ | +| v0.4.0 | Aderyn `0.6.5` | 0 High · 6 Low — **nothing to fix** | [report](./doc/audits/tools/v0.4.0/aderyn/aderyn-report.md) | [feedback](./doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md) | +| v0.4.0 | Slither | not run | — | — | ```bash -slither . --checklist --filter-paths "openzeppelin-contracts|test|CMTAT|forge-std" > slither-report.md +# Aderyn — mocks excluded (this project's mocks live in test/, which Aderyn does not scan) +aderyn -x mocks --output doc/audits/tools/v0.4.0/aderyn/aderyn-report.md + +# Slither +slither . --checklist --filter-paths "node_modules,test,forge-std,CMTAT,openzeppelin-contracts" \ + > doc/audits/tools/v0.4.0/slither/slither-report.md ``` +> **Static-analysis output is leads, not findings.** Every dismissal in the feedback files was +> verified against the cited `file:line`, and neither tool can see the specification-level issues +> that matter most here — those are in [`ERC_RESULT.md`](./ERC_RESULT.md). + ### Surya See [./doc/script](./doc/script) diff --git a/doc/ERCSpecification/erc-1643.md b/doc/ERCSpecification/erc-1643.md index 28f63a7..83af8c6 100644 --- a/doc/ERCSpecification/erc-1643.md +++ b/doc/ERCSpecification/erc-1643.md @@ -30,7 +30,7 @@ Historically, this proposal was authored as part of a broader security token sta ## Specification -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. +The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", and "MAY" in this document are to be interpreted as described in RFC 2119 and RFC 8174. Implementations MUST support querying and subscribing to updates on any relevant documentation for the security. @@ -45,34 +45,49 @@ A document entry is identified by a name (`bytes32`) and stores: ```solidity /// @title IERC1643 Document Management interface IERC1643 { + /// @notice Emitted when a document is created or updated. + /// @param name Identifier of the document. + /// @param uri Document location. + /// @param documentHash Hash of the document contents. + event DocumentUpdated(bytes32 indexed name, string uri, bytes32 documentHash); + + /// @notice Emitted when a document is removed. + /// @param name Identifier of the document. + /// @param uri Document location at the time of removal. + /// @param documentHash Hash of the document contents at the time of removal. + event DocumentRemoved(bytes32 indexed name, string uri, bytes32 documentHash); + /// @notice Reverts when `setDocument` is called with `name == bytes32(0)`. error ERC1643InvalidName(); /// @notice Reverts when `removeDocument` is called for a missing document. error ERC1643MissingDocument(); - /// @notice Returns metadata for a document identified by `name`. - /// @return uri Document location. - /// @return documentHash Hash of the document contents. - /// @return lastModified Last update timestamp. - function getDocument(bytes32 name) external view returns (string memory uri, bytes32 documentHash, uint256 lastModified); - /// @notice Creates or updates a document entry. /// @dev MUST emit `DocumentUpdated` on success. + /// @param name Identifier of the document. + /// @param uri Document location. + /// @param documentHash Hash of the document contents. function setDocument(bytes32 name, string calldata uri, bytes32 documentHash) external; /// @notice Removes an existing document entry. /// @dev MUST emit `DocumentRemoved` on success. + /// @param name Identifier of the document to remove. function removeDocument(bytes32 name) external; + /// @notice Returns metadata for a document identified by `name`. + /// @param name Identifier of the document. + /// @return uri Document location. + /// @return documentHash Hash of the document contents. + /// @return lastModified Last update timestamp. + function getDocument(bytes32 name) + external + view + returns (string memory uri, bytes32 documentHash, uint256 lastModified); + /// @notice Returns all document names currently tracked by the contract. + /// @return documentNames Names of all documents that are currently set. function getAllDocuments() external view returns (bytes32[] memory documentNames); - - /// @notice Emitted when a document is created or updated. - event DocumentUpdated(bytes32 indexed name, string uri, bytes32 documentHash); - - /// @notice Emitted when a document is removed. - event DocumentRemoved(bytes32 indexed name, string uri, bytes32 documentHash); } ``` @@ -82,19 +97,13 @@ Implementations SHOULD support ERC-165 interface detection. When ERC-165 is implemented, `supportsInterface` SHOULD return `true` for `type(IERC1643).interfaceId` and for the ERC-165 interface id. -`type(IERC1643MultiDocument).interfaceId` is the XOR of only the extension's own address-scoped functions; per ERC-165 semantics it does not include the base `IERC1643` selectors. Advertising the extension id therefore says nothing about whether the base single-argument functions are implemented. Accordingly: - -- A contract that implements the extension SHOULD return `true` for `type(IERC1643MultiDocument).interfaceId`. -- A contract MUST return `true` for `type(IERC1643).interfaceId` only if it also implements the base single-argument functions. A shared management contract that exposes only the address-scoped surface MUST NOT advertise `type(IERC1643).interfaceId`, since it does not implement that interface. - -Events are not part of any ERC-165 interface id, so `supportsInterface` reflects only which functions a contract implements, not whether it emits the base or extension events. - ### Function Requirements - `getDocument`: - MUST return the latest values for the provided document name. - MUST return empty values when the entry does not exist (`""`, `bytes32(0)`, `0`). - MUST NOT revert solely because the entry does not exist. + - Implementations SHOULD ensure that a stored entry always has a non-zero `lastModified`, so that `lastModified == 0` identifies an absent entry. Since `uri` and `documentHash` MAY both be empty for a stored document, they cannot distinguish an absent entry from one stored with empty metadata, and `lastModified` is the only value that can. Integrators should treat this as advisory rather than guaranteed when interacting with deployments predating this guidance. - `setDocument`: - MUST create a new entry when `name` is not present. @@ -118,78 +127,25 @@ Events are not part of any ERC-165 interface id, so `supportsInterface` reflects - `getAllDocuments`: - MUST include every document name added by `setDocument` and not removed by `removeDocument`. - MUST NOT include removed document names. - -### Optional Extension: Multi-Token Document Management - -This extension is **not part of the original [ERC-1643](./eip-1643.md)**; it is an optional, additive addition for the case where document management is **delegated to a separate smart contract** that serves **more than one** token contract. - -The base `DocumentUpdated` / `DocumentRemoved` events carry no address, so consumers attribute them to the address of the contract that emitted them. This is unambiguous when a single contract both exposes ERC-1643 and stores its own documents, but breaks when one shared contract manages documents for several token contracts: the base events cannot identify which token contract a change belongs to. The extension adds address-scoped functions and address-carrying events, where `subject` is the address of the contract the documents belong to (typically a token contract, but the reasoning applies to any ERC-721/ERC-1155 token, vault, or other on-chain product). - -The extension is intentionally declared **independently of `IERC1643`** (it does not inherit it), so a shared management contract can implement the address-scoped surface **without** being forced to implement the base single-argument functions. A contract MAY implement both interfaces — for example a manager that also lets a token contract manage its own documents by calling the base functions with `msg.sender` as the implied subject — in which case it implements, and advertises, each interface separately. - -```solidity -/// @title IERC1643MultiDocument Multi-Token Document Management (optional extension) -interface IERC1643MultiDocument { - /// @notice Returns metadata for the document identified by `name` belonging to `subject`. - /// @param subject Address of the contract the documents belong to. - /// @return uri Document location. - /// @return documentHash Hash of the document contents. - /// @return lastModified Last update timestamp. - function getDocument(address subject, bytes32 name) external view returns (string memory uri, bytes32 documentHash, uint256 lastModified); - - /// @notice Creates or updates a document entry for `subject`. - /// @dev MUST emit `DocumentUpdatedForSubject` on success. - function setDocument(address subject, bytes32 name, string calldata uri, bytes32 documentHash) external; - - /// @notice Removes an existing document entry for `subject`. - /// @dev MUST emit `DocumentRemovedForSubject` on success. - function removeDocument(address subject, bytes32 name) external; - - /// @notice Returns all document names currently tracked for `subject`. - function getAllDocuments(address subject) external view returns (bytes32[] memory documentNames); - - /// @notice Emitted when a document is created or updated for `subject`. - event DocumentUpdatedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); - - /// @notice Emitted when a document is removed for `subject`. - event DocumentRemovedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); -} -``` - -#### Emission Responsibility - -The base events MUST be emitted by the contract that exposes ERC-1643 to consumers — the address consumers are expected to subscribe to. When document management is delegated, the emitter MUST be chosen so per-contract observability is preserved: - -- A management contract **dedicated to a single** token contract MAY be that token contract's ERC-1643 implementation and MUST emit the base `DocumentUpdated` / `DocumentRemoved` events; the base events are unambiguous because only one subject is served. -- A management contract **shared by multiple** token contracts MUST NOT report per-subject changes through the base events, since those events cannot identify the subject. In this configuration each token contract MUST emit the base events for its own documents, and the shared contract SHOULD emit the extension events (`DocumentUpdatedForSubject` / `DocumentRemovedForSubject`) instead, as those carry the `subject` address. -- Implementations SHOULD NOT emit the base events from **both** the token contract and the management contract. - -#### Extension Function Requirements - -- `getDocument(address subject, bytes32 name)` and `getAllDocuments(address subject)`: behave as their base counterparts, scoped to `subject`. -- `setDocument(address subject, ...)`: MUST emit `DocumentUpdatedForSubject` after state changes; SHOULD revert with `ERC1643InvalidName()` when `name == bytes32(0)`. -- `removeDocument(address subject, bytes32 name)`: MUST emit `DocumentRemovedForSubject`; SHOULD revert with `ERC1643MissingDocument()` when the named document does not exist for `subject`. -- Implementations MUST authorize writes per `subject`, so that a caller cannot create, update, or remove documents for a `subject` it is not permitted to manage. + - The order of the returned names is unspecified. Removing a document MAY change the position of unrelated names, so consumers MUST NOT rely on a stable ordering, and MUST NOT treat a change in position as a change to a document. ## Rationale -The standard uses `bytes32` names to keep keys compact and deterministic, while leaving naming conventions to implementations. A URI-based pointer is used instead of on-chain document storage to avoid high gas costs and to support existing off-chain document systems. - -Including a document hash enables clients to verify that fetched off-chain content matches issuer-published metadata. Emitting update and removal events supports indexing and near-real-time monitoring without repeated full-state polling. - -While a human-readable document title cannot always be represented directly in `bytes32` without hashing or canonicalization, `bytes32` remains practical for on-chain identifiers because fixed-size values can be compared directly (`a == b`). By contrast, `string` comparisons generally require hashing (for example, `keccak256(bytes(s))`), which increases contract code size and gas usage when repeated comparisons are needed on-chain, such as locating and removing a document name from an array. +- The standard uses `bytes32` names to keep keys compact and deterministic, while leaving naming conventions to implementations. A URI-based pointer is used instead of on-chain document storage to avoid high gas costs and to support existing off-chain document systems. +- Including a document hash enables clients to verify that fetched off-chain content matches issuer-published metadata. +- Emitting update and removal events supports indexing and near-real-time monitoring without repeated full-state polling. +- While a human-readable document title cannot always be represented directly in `bytes32` without hashing or canonicalization, `bytes32` remains practical for on-chain identifiers because fixed-size values can be compared directly (`a == b`). By contrast, `string` comparisons generally require hashing (for example, `keccak256(bytes(s))`), which increases contract code size and gas usage when repeated comparisons are needed on-chain, such as locating and removing a document name from an array. ## Backwards Compatibility This ERC is additive and does not alter base token transfer semantics. It can be implemented alongside existing token standards and permissioning systems without changing their core behavior. -The optional multi-token extension (`IERC1643MultiDocument`) is likewise additive and was not part of the original ERC-1643. Its address-scoped functions have different signatures — hence different selectors — than the base functions, so they sit alongside them without collision, and its `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` events are new topics that leave the base `DocumentUpdated` / `DocumentRemoved` events unchanged. A consumer that only knows base ERC-1643 is unaffected: it continues to call the base functions and subscribe to the base events on the address it was directed to watch. As required in the Specification, an implementation of the extension leaves the base functions and events unchanged, and every address a consumer is directed to subscribe to still emits the base events. - ## Test Cases Implementations should verify at least the following: - Adding a new document and reading it through `getDocument`. +- Reading a name that was never set, confirming `getDocument` returns empty values (`""`, `bytes32(0)`, `0`) and does not revert. - Updating an existing document and validating changed URI/hash/timestamp. - Removing a document and ensuring it is no longer returned by `getAllDocuments`. - Emission of `DocumentUpdated` on create/update and `DocumentRemoved` on delete. @@ -197,7 +153,11 @@ Implementations should verify at least the following: ## Reference Implementation -The interface is provided in [the reference interface](../assets/eip-1643/src/erc-1643/IERC1643.sol). A reusable abstract module implementing the full interface is provided in [the reference module](../assets/eip-1643/src/erc-1643/ERC1643.sol). Example integrations attaching the module to [ERC-20](./eip-20.md) and [ERC-721](./eip-721.md) tokens are provided in [the ERC-20 example](../assets/eip-1643/src/ERC20DocumentToken.sol) and [the ERC-721 example](../assets/eip-1643/src/ERC721DocumentToken.sol). These examples use the OpenZeppelin library and restrict document mutation to the contract owner. They are provided for educational purposes only and have not been audited. +The interface is provided in [the reference interface](../assets/eip-1643/src/erc-1643/IERC1643.sol). A reusable abstract module implementing the full interface is provided in [the reference module](../assets/eip-1643/src/erc-1643/ERC1643.sol). + +Example integrations attaching the module to [ERC-20](./eip-20.md) and [ERC-721](./eip-721.md) tokens are provided in [the ERC-20 example](../assets/eip-1643/src/ERC20DocumentToken.sol) and [the ERC-721 example](../assets/eip-1643/src/ERC721DocumentToken.sol). + +These examples use the OpenZeppelin library and restrict document mutation to the contract owner. They are provided for educational purposes only and have not been audited. The module maintains: @@ -210,10 +170,10 @@ The module maintains: - Document URIs may reference mutable off-chain content. Consumers are strongly encouraged to verify content using the published `documentHash` and trusted retrieval channels. - Implementations should protect `setDocument` and `removeDocument` with appropriate authorization, otherwise unauthorized actors can modify legal or operational references. - Applications should treat event streams as advisory and reconcile against on-chain state when correctness is critical. +- `getAllDocuments` returns the entire set of names in a single call, and its response grows with the number of stored documents. It is the only enumeration path defined here, so a contract holding a large document set may produce a call that exceeds the gas limit an `eth_call` provider applies, leaving no standard way to enumerate. Implementations expecting large sets should consider exposing an additional paginated accessor alongside this interface. - Document names may not always fit cleanly into `bytes32`, especially for long legal titles. Implementations should avoid lossy truncation of human-readable names; using a deterministic hash-based identifier (for example, the document content hash or a hash of a canonical full title) as the `bytes32` name is a safer alternative. -- The custom errors `ERC1643InvalidName()` and `ERC1643MissingDocument()` are defined in this interface but were absent from the original [ERC-1643](./eip-1643.md) proposal text. Older implementations may not define these errors and may instead revert with strings or implementation-specific error patterns. Integrators should not assume all ERC-1643 contracts expose identical revert data. +- The custom errors `ERC1643InvalidName()` and `ERC1643MissingDocument()` are defined in this interface but were absent from the earlier draft text of this proposal. Older implementations may not define these errors and may instead revert with strings or implementation-specific error patterns. Integrators should not assume all [ERC-1643](./eip-1643.md) contracts expose identical revert data. - ERC-165 interface detection was also not part of the earlier ERC-1643 draft text. Older implementations may not expose `supportsInterface` for ERC-165 or `IERC1643`, so integrators should treat ERC-165 support as optional when interacting with legacy deployments. -- A shared document-management contract implementing the optional multi-token extension governs documents for several subjects behind a single address. It has to authorize writes per `subject` (as required in the Specification); otherwise a caller could modify another subject's legal or operational references. Because events are not covered by ERC-165, `supportsInterface(type(IERC1643MultiDocument).interfaceId)` confirms only that the extension functions exist, not that the address-carrying events are actually emitted — integrators relying on the extension events should confirm emission out of band. ## Copyright diff --git a/doc/ERCSpecification/erc-draft_multi_document_management.md b/doc/ERCSpecification/erc-draft_multi_document_management.md new file mode 100644 index 0000000..47bc57c --- /dev/null +++ b/doc/ERCSpecification/erc-draft_multi_document_management.md @@ -0,0 +1,268 @@ +--- +title: Multi-Subject Document Management +description: Interface for a contract that attaches, updates, removes, and enumerates documents on behalf of multiple subject contracts. +author: Ryan Sauge (@rya-sge) +discussions-to: https://ethereum-magicians.org/t/erc-1643-document-management-standard-erc-1400/27437 +status: Draft +type: Standards Track +category: ERC +created: 2026-07-28 +requires: 165, 1643 +--- + +## Abstract + +This ERC defines an interface for a contract that stores and manages documents on behalf of **several other contracts**, called *subjects*. Every function is scoped by a `subject` address, and every event carries that address, so a single management contract can serve many subjects while remaining observable and operable per subject. + +It is a companion to [ERC-1643](./eip-1643.md), which defines the equivalent per-contract interface. A subject is not required to implement ERC-1643, or any other particular interface, to have documents managed on its behalf. + +## Motivation + +[ERC-1643](./eip-1643.md) associates documents with the contract that exposes the interface. Its `DocumentUpdated` and `DocumentRemoved` events carry only `(name, uri, documentHash)` — no address — so consumers attribute an event to the address that emitted it. This is unambiguous when a single contract both exposes the interface and stores its own documents. + +A common operational and gas optimization is to **delegate** document management to a separate contract. When that contract is dedicated to one subject, nothing new is needed: it effectively is that subject's ERC-1643 implementation, and its events are unambiguous. But when **one management contract serves many subjects**, the per-contract events break down — a consumer watching the management contract cannot tell which subject a change belongs to, and the management contract has no compliant way to report per-subject changes at all. + +Shared document management is worth supporting directly. An issuer operating many tokens, funds, or vaults typically maintains one document library and one set of operators, and duplicating that storage and access-control logic into every subject contract is redundant and expensive. What the shared case needs is an address in the event and an address in the function signature. That is the whole of this proposal. + +The scope is deliberately broader than tokens. A subject is any contract that documents can belong to — an [ERC-20](./eip-20.md) or [ERC-721](./eip-721.md) token, an [ERC-1155](./eip-1155.md) multi-token, a vault, or any other on-chain product. Nothing in this interface inspects the subject or calls into it. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +A document entry is identified by a `subject` (`address`) together with a name (`bytes32`), and stores: + +- A URI (`string`) pointing to the document location. +- A content hash (`bytes32`) for integrity checks. +- A last-modified timestamp (`uint256`) set when the entry is written. + +This is the [ERC-1643](./eip-1643.md) data model, extended with the `subject` address. Entries under different subjects are independent: a `name` written for one subject MUST NOT affect the entry stored under the same `name` for any other subject. + +### Interface + +This interface is declared **independently of `IERC1643`** — it does not inherit it — so a management contract can implement the address-scoped surface without being forced to implement the per-contract single-argument functions. + +A contract MAY implement both, in which case it implements, and advertises, each interface separately. + +Two of the three errors below, `ERC1643InvalidName` and `ERC1643MissingDocument`, are **the same errors defined by [ERC-1643](./eip-1643.md)** — same names, same signatures, hence the same 4-byte selectors — reused deliberately so that a caller sees identical revert data for identical conditions whether it is talking to an ERC-1643 contract or to a management contract. They are restated here rather than inherited because this interface does not inherit `IERC1643`; a contract implementing both obtains each error once, from `IERC1643`, and MUST NOT declare them twice. + +`MultiDocumentInvalidSubject` has no ERC-1643 counterpart: the condition it reports cannot arise there, because ERC-1643's `setDocument` has no `subject` argument — its subject is implicitly the contract itself, which is never the null address. + +```solidity +/// @title IERC1643MultiDocument Multi-Subject Document Management +interface IERC1643MultiDocument { + /// @notice Emitted when a document is created or updated for `subject`. + /// @param subject Address of the contract the document belongs to. + /// @param name Identifier of the document. + /// @param uri Document location. + /// @param documentHash Hash of the document contents. + event DocumentUpdatedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); + + /// @notice Emitted when a document is removed for `subject`. + /// @param subject Address of the contract the document belonged to. + /// @param name Identifier of the document. + /// @param uri Document location at the time of removal. + /// @param documentHash Hash of the document contents at the time of removal. + event DocumentRemovedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); + + /// @notice Reverts when `setDocument` or `removeDocument` is called with `subject == address(0)`. + /// @dev Specific to this proposal; has no ERC-1643 counterpart. + error MultiDocumentInvalidSubject(); + + /// @notice Reverts when `setDocument` is called with `name == bytes32(0)`. + /// @dev Same error as defined by ERC-1643, reused unchanged. + error ERC1643InvalidName(); + + /// @notice Reverts when `removeDocument` is called for a missing document. + /// @dev Same error as defined by ERC-1643, reused unchanged. + error ERC1643MissingDocument(); + + /// @notice Creates or updates a document entry for `subject`. + /// @dev MUST emit `DocumentUpdatedForSubject` on success. + /// @param subject Address of the contract the document belongs to. + /// @param name Identifier of the document. + /// @param uri Document location. + /// @param documentHash Hash of the document contents. + function setDocument(address subject, bytes32 name, string calldata uri, bytes32 documentHash) external; + + /// @notice Removes an existing document entry for `subject`. + /// @dev MUST emit `DocumentRemovedForSubject` on success. + /// @param subject Address of the contract the document belongs to. + /// @param name Identifier of the document to remove. + function removeDocument(address subject, bytes32 name) external; + + /// @notice Returns metadata for the document identified by `name` belonging to `subject`. + /// @param subject Address of the contract the documents belong to. + /// @param name Identifier of the document. + /// @return uri Document location. + /// @return documentHash Hash of the document contents. + /// @return lastModified Last update timestamp. + function getDocument(address subject, bytes32 name) + external + view + returns (string memory uri, bytes32 documentHash, uint256 lastModified); + + /// @notice Returns all document names currently tracked for `subject`. + /// @param subject Address of the contract the documents belong to. + /// @return documentNames Names of all documents that are currently set for `subject`. + function getAllDocuments(address subject) external view returns (bytes32[] memory documentNames); +} +``` + +### Function Requirements + +- `getDocument`: + - MUST return the latest values for the provided `subject` and `name`. + - MUST return empty values when the entry does not exist (`""`, `bytes32(0)`, `0`). + - MUST NOT revert solely because the entry does not exist. + +- `setDocument`: + - MUST create a new entry when `name` is not present for `subject`. + - MUST overwrite the existing entry when `name` already exists for `subject`. + - MUST update the stored last-modified timestamp. + - MUST emit `DocumentUpdatedForSubject` after state changes. + - MUST revert if the update cannot be persisted. + - SHOULD revert when `name == bytes32(0)`, using `ERC1643InvalidName()`. + - `uri` and `documentHash` MAY be empty (`""` and `bytes32(0)`), depending on issuer workflow and document lifecycle stage. Implementations MAY reject empty values based on policy requirements. + +- `removeDocument`: + - MUST remove the entry identified by `subject` and `name`. + - MUST emit `DocumentRemovedForSubject` with the removed metadata. + - MUST revert if removal cannot be completed. + - SHOULD revert when the named document does not exist for `subject`, using `ERC1643MissingDocument()`. + +- `getAllDocuments`: + - MUST include every name added for `subject` by `setDocument` and not removed by `removeDocument`. + - MUST NOT include removed names, or names belonging to any other subject. + +- `setDocument` and `removeDocument` SHOULD revert when `subject == address(0)`, since the null address is never a valid document subject, using `MultiDocumentInvalidSubject()`. + - Guarding the write path is sufficient: when `setDocument` rejects `subject == address(0)`, no document can exist under that subject, so `removeDocument` there already fails with `ERC1643MissingDocument()`. + +- Implementations MAY use different error names/signatures than those shown in this specification. + +### Authorization + +Implementations MUST authorize writes per `subject`, so that a caller cannot create, update, or remove documents for a `subject` it is not permitted to manage. A single management contract holding the document sets of unrelated subjects behind one address makes this the central security property of the proposal; see Security Considerations. + +### Interface Detection ([ERC-165](./eip-165.md)) + +Implementations SHOULD support ERC-165 interface detection. When ERC-165 is implemented, `supportsInterface` SHOULD return `true` for `type(IERC1643MultiDocument).interfaceId` and for the ERC-165 interface id. + +`type(IERC1643MultiDocument).interfaceId` is the XOR of only this interface's own address-scoped functions. Because this interface does not inherit `IERC1643`, and because Solidity excludes inherited selectors from `type(I).interfaceId` in any case, advertising it says nothing about whether the per-contract single-argument functions are implemented. Accordingly, a contract MUST return `true` for `type(IERC1643).interfaceId` only if it also implements those base functions; a management contract exposing only the address-scoped surface MUST NOT advertise `type(IERC1643).interfaceId`. + +Events are not part of any ERC-165 interface id, so `supportsInterface` reflects only which functions a contract implements, not whether it emits the events defined here. + +### Optional: Subject-Side Manager Discovery + +Nothing above lets a consumer that knows only a subject find the management contract holding that subject's documents; the address has to be learned out of band. Subjects MAY close this gap by implementing the following interface. It is **implemented by the subject, not by the management contract**, and is optional for both. + +```solidity +/// @title IMultiDocumentSubject Manager discovery (optional) +interface IMultiDocumentSubject { + /// @notice Emitted when the managing contract changes. + /// @param previousManager Address that previously managed this contract's documents. + /// @param newManager Address that manages this contract's documents from now on. + event DocumentManagerUpdated(address indexed previousManager, address indexed newManager); + + /// @notice Returns the contract managing this contract's documents. + /// @dev MUST NOT revert. MUST emit `DocumentManagerUpdated` when the returned value changes. + /// @return manager Address of the managing contract, or `address(0)` when documents are managed in-contract. + function documentManager() external view returns (address manager); +} +``` + +- `documentManager`: + - MUST return the address of the contract to which this contract's document management is delegated. + - MUST return `address(0)` when document management is not delegated. + - MUST NOT revert. +- A contract that changes the returned value MUST emit `DocumentManagerUpdated`, so a consumer that cached the address learns of the migration. Without this, discovery would be a one-shot read that silently goes stale. +- This interface is declared independently of both `IERC1643` and `IERC1643MultiDocument`, so its ERC-165 id is distinct and advertising it perturbs neither. A subject implementing it SHOULD return `true` for `type(IMultiDocumentSubject).interfaceId`. + +The returned address is an **assertion by the subject**, not a verified link: nothing requires the named contract to acknowledge the relationship, or to have any documents for that subject at all. Consumers MUST treat it as a discovery hint and not as evidence of authorization; see Security Considerations. + +### Relationship to [ERC-1643](./eip-1643.md) + +A subject need not implement ERC-1643. This section applies only when it does, and constrains such deployments; it places no requirement on ERC-1643 itself, which is unchanged by this proposal. + +#### Emission Responsibility + +ERC-1643's `DocumentUpdated` / `DocumentRemoved` carry no address, so consumers attribute them to the address that emitted them. Those events MUST therefore be emitted by the contract that exposes ERC-1643 to consumers — the address consumers are expected to subscribe to. When an ERC-1643 subject delegates to a management contract, the emitter MUST be chosen so per-contract observability is preserved: + +- A management contract **dedicated to a single** subject MAY be that subject's ERC-1643 implementation and MUST emit `DocumentUpdated` / `DocumentRemoved`; those events are unambiguous because only one subject is served. Such a contract does not need this proposal. +- A management contract **serving several** subjects MUST NOT report per-subject changes through ERC-1643's events, since those events cannot identify the subject. In this configuration each subject MUST emit ERC-1643's events for its own documents, and the management contract emits `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` instead, as those carry the `subject` address. +- Implementations SHOULD NOT emit ERC-1643's events from **both** the subject and the management contract; it is redundant and wastes gas. + +#### Call Topology + +A contract can only emit an event in a transaction in which it executes. The requirement that each subject emit ERC-1643's events for its own documents therefore constrains how a write reaches the management contract, not only which contract is nominally responsible for emitting. + +A write that creates, updates, or removes a document for an ERC-1643 subject MUST include an execution point in that subject at which the event is emitted. Two topologies satisfy this: + +- **Subject-initiated.** The subject calls the management contract's address-scoped write and emits ERC-1643's event itself. The management contract emits the corresponding event defined here. +- **Manager-initiated with callback.** An authorized operator calls the management contract, which calls back into the subject through an implementation-defined permissioned hook; the subject emits ERC-1643's event. This proposal does not define the signature of that hook. + +A deployment in which an operator calls `setDocument(address subject, ...)` or `removeDocument(address subject, bytes32 name)` directly, with no execution point in the subject, does **not** satisfy ERC-1643's emission requirement for that subject: a consumer subscribed to the subject never observes the change, so the subject does not support subscribing to updates on its documentation and is not a conformant ERC-1643 implementation in that deployment, even though it exposes the ERC-1643 functions. Note this is a consequence of ERC-1643's own requirements, not an additional obligation imposed here. + +Accordingly, a management contract SHOULD restrict its address-scoped writes to callers for which one of the two topologies above holds — the subject itself, or an operator whose write path calls back into the subject. This is narrower than, and consistent with, the per-`subject` authorization required above: authorization decides *who* may write for a subject, while call topology decides whether that write remains observable on the subject's own address. + +Consumers of this proposal's events are unaffected in either topology: `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` carry the `subject` address and are emitted by the management contract in every case. + +## Rationale + +The `subject` parameter is named generically rather than "token" because the contract documents belong to is not necessarily a token, and because the on-chain identifiers should not hard-code an assumption the interface does not enforce. The event names follow the parameter (`DocumentUpdatedForSubject`), keeping the event and its attribute aligned. + +The interface is declared independently of `IERC1643` rather than inheriting it. Inheritance would force every shared management contract to implement the per-contract single-argument functions, which have no meaningful subject in the shared case, and would invite contracts to advertise an ERC-165 interface id for functions they do not implement. + +Separate address-scoped functions are used instead of overloading the per-contract ones with a subject-carrying variant of the same name because the resulting selectors are distinct either way; declaring them here keeps this proposal self-contained and readable without reference to ERC-1643's interface. + +The data model is inherited from ERC-1643 unchanged — `bytes32` names for compact, directly comparable on-chain identifiers, a URI pointer instead of on-chain storage, and a content hash for integrity — so that a subject can migrate between self-managed and delegated document storage without changing what consumers read. + +Errors are prefixed by the proposal that defines the condition, not by the proposal that declares them. `ERC1643InvalidName` and `ERC1643MissingDocument` keep their ERC-1643 prefix because they are ERC-1643's errors, reused so revert data stays identical across both interfaces; renaming them here would fragment that. `MultiDocumentInvalidSubject` is defined by this proposal alone and is therefore named after it, rather than borrowing a prefix from a standard in which the condition cannot occur. + +Manager discovery is defined here, and as a separate optional interface, for three reasons. It is a function on the *subject*, so folding it into ERC-1643 would grow that proposal with a function about a delegation arrangement ERC-1643 does not itself define. It is meaningful only where delegation exists, which is the subject of this proposal. And keeping it out of `IERC1643MultiDocument` means a management contract is never asked to implement a getter about itself that only its subjects can answer, while the distinct ERC-165 id lets a consumer detect discovery support without inferring anything about either document interface. + +The names `IERC1643MultiDocument`, `MultiDocumentInvalidSubject` and `IMultiDocumentSubject` are all provisional. They record the companion relationship while this proposal is unnumbered, and SHOULD be revisited together to track this proposal's own number once an editor assigns one. The two reused ERC-1643 error names are **not** provisional and are expected to stay as they are. + +## Backwards Compatibility + +This proposal introduces a new interface and does not modify [ERC-1643](./eip-1643.md) or any other proposal. + +- **Function selectors.** `getDocument(address,bytes32)`, `getAllDocuments(address)`, `setDocument(address,bytes32,string,bytes32)` and `removeDocument(address,bytes32)` have different signatures — hence different 4-byte selectors — than their single-argument ERC-1643 counterparts. A contract implementing both exposes both sets side by side with no collision. +- **Events.** `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` are new event topics. ERC-1643's `DocumentUpdated` / `DocumentRemoved` are untouched and keep their exact meaning. +- **ERC-165.** Advertising `type(IERC1643MultiDocument).interfaceId` does not disturb `supportsInterface(type(IERC1643).interfaceId)`. A consumer that only knows ERC-1643 detects it exactly as before. +- **Manager discovery.** `IMultiDocumentSubject` is optional and separate. It adds one selector and one event topic to a subject that chooses to implement it, and is declared independently of both document interfaces, so it perturbs neither interface id. A subject that does not implement it is unaffected, and a consumer that does not know it behaves exactly as before. + +A consumer that only knows ERC-1643 is unaffected: it continues to call the per-contract functions and subscribe to the per-contract events on the address it was directed to watch. The one way to break such a consumer is to direct it at a management contract that emits only the events defined here — a deployment error, addressed by the Emission Responsibility and Call Topology requirements above rather than by the interface itself. + +## Test Cases + +Implementations should verify at least the following: + +- Subject isolation: the same `name` written for two different subjects yields two independent entries, and `getAllDocuments` for one subject never returns the other's names. +- Adding, updating, and removing a document for a subject, and reading it back through `getDocument`. +- Emission of `DocumentUpdatedForSubject` on create/update and `DocumentRemovedForSubject` on delete, each carrying the correct `subject`. +- A caller not authorized for a subject failing to create, update, or remove that subject's documents. +- `setDocument` rejecting `subject == address(0)` and `name == bytes32(0)`. +- `supportsInterface` returning `true` for `type(IERC1643MultiDocument).interfaceId`, and returning `false` for `type(IERC1643).interfaceId` on a contract that implements only the address-scoped surface. +- For a subject implementing `IMultiDocumentSubject`: `documentManager` returning the delegate's address, returning `address(0)` when management is not delegated, and `DocumentManagerUpdated` being emitted with the correct previous and new addresses when the delegate changes. + +## Reference Implementation + +A reference implementation is not yet provided. It is expected to maintain, per subject, a mapping from `bytes32` name to document metadata, a set for enumeration of active names, and index tracking to support O(1) removals — the ERC-1643 reference module's structure, keyed additionally by `subject` — together with the per-`subject` authorization required in the Specification. + +## Security Considerations + +- **Per-subject authorization is the central risk.** A management contract holds the document sets of unrelated subjects behind a single address. If writes are not authorized per `subject`, any caller permitted to write for one subject can modify another subject's legal or operational references. This is a stronger requirement than in the per-contract case, where a contract's own access control naturally scopes to its own documents. +- **Delegation moves the subject's trust boundary.** A subject that delegates document management is only as protected as the management contract's access control: whoever can write for that subject there can change its legal or operational references, regardless of the subject's own permissioning. Delegating to a contract serving several subjects also concentrates the document sets of unrelated parties behind a single address, so a single access-control flaw there is not contained to one subject. Subjects should treat the choice of management contract as a permissioning decision, not merely a storage one. +- **The null subject.** Implementations should reject `subject == address(0)`. This is a data-integrity concern rather than a fund-safety one: subject namespaces are isolated, and the null address cannot call the contract to read documents registered under it, so such entries are inert. They do, however, let callers populate a namespace no contract can ever own, cluttering state and misleading off-chain indexers that key on `subject`. +- **Direct writes can silently bypass subject-side emission.** When a subject implements [ERC-1643](./eip-1643.md), a write sent directly to the management contract without an execution point in the subject leaves the subject's per-contract events unemitted. The failure is quiet in both directions: the write succeeds and the event defined here is emitted, so nothing reverts, while a consumer watching the subject sees no event and concludes the documents are unchanged. Integrators who must rely on the per-contract events should confirm the deployment's write path out of band. +- **Event emission is not discoverable.** Because events are outside ERC-165, `supportsInterface(type(IERC1643MultiDocument).interfaceId)` confirms only that the functions exist, not that the address-carrying events are actually emitted. Integrators relying on those events should confirm emission out of band. +- **`documentManager` is an unverified assertion.** The value is chosen entirely by the subject, and nothing requires the named contract to acknowledge the relationship or to hold any documents for that subject. A compromised or malicious subject can point consumers at an attacker-controlled contract serving fabricated documents, and calling `getAllDocuments(subject)` on the claimed manager does not disprove this — anyone can populate their own contract with entries for any subject. Manager discovery is a convenience for locating a feed, not an authorization or authenticity signal. Consumers making decisions that depend on document contents should verify against the published `documentHash` and, where the stakes justify it, confirm the manager address through the same out-of-band channel they would have used without this interface. +- **A stale cached manager address.** A consumer that reads `documentManager` once and does not watch `DocumentManagerUpdated` may keep following a superseded contract after a migration, seeing a frozen document set with no indication it is no longer current. +- **Mutable off-chain content.** Document URIs may reference content that changes independently of the chain. Consumers are strongly encouraged to verify content against the published `documentHash` and to use trusted retrieval channels. +- **Names may not fit `bytes32`.** Long legal titles should not be lossily truncated; a deterministic hash-based identifier (for example the document content hash, or a hash of a canonical full title) is a safer choice for the `bytes32` name. +- **Events are advisory.** Applications should reconcile event streams against on-chain state when correctness is critical. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/doc/audits/AUDIT_OVERVIEW.md b/doc/audits/AUDIT_OVERVIEW.md new file mode 100644 index 0000000..1aab618 --- /dev/null +++ b/doc/audits/AUDIT_OVERVIEW.md @@ -0,0 +1,66 @@ +# Security & audit overview — DocumentEngine + +> **This project has not been audited.** No formal external security audit has been performed on any +> release. What follows is the record of the automated and AI-assisted analyses that *have* been run. +> These are **not** a substitute for an audit. Anyone deploying this engine in production must +> commission their own independent security assessment. + +## In scope + +The `src/` tree only — 9 files, 298 nSLOC as of `v0.4.0`: + +``` +src/DocumentEngine.sol src/interfaces/IERC1643MultiDocument.sol +src/DocumentEngineBase.sol src/interfaces/IERC8303.sol +src/DocumentEngineInvariant.sol src/interfaces/ITokenBinding.sol +src/DocumentEngineOwnable.sol src/modules/TokenBindingModule.sol + src/modules/VersionModule.sol +``` + +Out of scope: `lib/` (CMTAT, RuleEngine, OpenZeppelin — audited, or not, upstream), `test/`, +`script/`. + +## Analyses + +| Analysis | Version | Report | Triage | +| --- | --- | --- | --- | +| Aderyn `0.6.5` | `v0.4.0` | [report](./tools/v0.4.0/aderyn/aderyn-report.md) | [feedback](./tools/v0.4.0/aderyn/aderyn-report-feedback.md) | +| Slither | — | not run | — | +| ERC conformance analysis (AI-assisted) | `v0.4.0` | [`ERC_RESULT.md`](../../ERC_RESULT.md) | — | + +## Static-analysis results + +| Tool | High | Medium | Low | Info | Anything to fix? | +| --- | --- | --- | --- | --- | --- | +| Aderyn `0.6.5` | 0 | — | 6 | 0 | **No.** 4 by design, 1 environment, 1 false positive; 1 of the "by design" instances overlaps a known scalability item (§4.7) | +| Slither | — | — | — | — | not run for `v0.4.0` | + +Aderyn reports no Medium or Info categories; it classifies only High and Low. + +## Substantive findings fixed in `v0.4.0` + +From the ERC conformance analysis ([`ERC_RESULT.md`](../../ERC_RESULT.md)) rather than from the +static analyzers — neither tool can see these, since both are ABI- and specification-level: + +| Finding | Severity | Status | +| --- | --- | --- | +| `getDocument` returned a `Document` struct where ERC-1643 mandates three flat values. Same selector and same `type(IERC1643).interfaceId` either way, so ERC-165 detection could not distinguish them and a spec-conformant consumer silently decoded corrupt values | High | **Fixed** — flat return on both overloads, pinned by `testGetDocumentReturnsFlatErc1643Abi`, which inspects the returndata directly | +| `ERC1643InvalidName` / `ERC1643MissingDocument` declared both locally and by `IERC1643`, which the multi-subject draft forbids and the compiler rejects | Blocker | **Fixed** — local declarations removed | + +## Known open items + +Not defects in the sense of being exploitable, but tracked deviations from the specifications. Full +detail in [`ERC_RESULT.md`](../../ERC_RESULT.md) §7. + +| Item | Severity | Where | +| --- | --- | --- | +| Authorization is not per-`subject`, and `_authorizeDocumentManagement()` takes no `subject`, so a deployment cannot make it per-subject by overriding the hook | High | §4.2 | +| Admin write path has no execution point in the subject, so an ERC-1643 subject emits nothing for writes sent straight to the engine | Medium | §4.3 | +| `ERC1643InvalidSubject` vs the draft's `MultiDocumentInvalidSubject` naming | Low | §4.4 | +| `_removeDocumentName` is O(n); no paginated enumeration | Low | §4.7 — also surfaced by Aderyn L-5 | +| `bindToken(address(0))` accepted; bind/unbind emit unconditionally | Low | §4.8 | +| The ERC-2771 trusted forwarder can act as any bound subject and is immutable | Info | §4.9 | + +## Reporting a vulnerability + +See the repository's security policy, or contact [admin@cmta.ch](mailto:admin@cmta.ch). diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md new file mode 100644 index 0000000..4008de5 --- /dev/null +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md @@ -0,0 +1,49 @@ +# Aderyn report — triage (DocumentEngine `v0.4.0`) + +| | | +| --- | --- | +| Report | [`aderyn-report.md`](./aderyn-report.md) | +| Command | `aderyn -x mocks --output doc/audits/tools/v0.4.0/aderyn/aderyn-report.md` | +| Tool version | `aderyn 0.6.5` | +| Scope | `src/` only — 9 files, 298 nSLOC. **Mocks and tests excluded.** This project keeps its mocks (`CMTATDocumentEngineMock`, `OpenDocumentEngine`) inside `test/DocumentEngine.t.sol`, which Aderyn does not scan, so `-x mocks` matched nothing and changed nothing. | +| Dependency | CMTAT `v3.3.0-rc2` (`35d8940b`) | +| Result | **0 High · 6 Low** | + +## Executive triage + +**Nothing to fix.** No finding is exploitable, and none blocks the `v0.4.0` release. + +Five of the six are the analyzer's standing advisories about deliberate design choices (a privileged +operator, a caret pragma, PUSH0, revert-in-loop, storage-writes-in-loop) and one is a false positive. + +The one result worth keeping in view is **L-5 at `DocumentEngineBase.sol:238`**, which is not a batch +loop but the linear scan in `_removeDocumentName`. Aderyn reached it from the "costly operation in a +loop" heuristic; it happens to land on the same code as `ERC_RESULT.md` §4.7, which flags the O(n) +removal against the multi-subject draft's expectation of "index tracking to support O(1) removals". +That is a scalability item, not a vulnerability — a subject with a large document set makes +`removeDocument` progressively more expensive, and `batchRemoveDocuments` compounds it to O(n·m). +Two independent routes arriving at the same line is a reasonable argument for doing the index-mapping +fix in a later release. It is deliberately **not** being done in `v0.4.0`, which is scoped to the +CMTAT upgrade. + +## Findings + +| ID | Detector | Sev | Instances | Disposition | Reason (verified against the cited lines) | +| --- | --- | --- | --- | --- | --- | +| L-1 | Centralization Risk | Low | 2 | **By design** | `DocumentEngine.sol:24`, `DocumentEngineOwnable.sol:24`. The whole premise of the contract is that a trusted operator manages documents for a fleet of subjects; `DOCUMENT_MANAGER_ROLE` (and `owner`) are that operator. Documented in the README and analysed in `ERC_RESULT.md` §4.2, which argues the privilege should be *narrowed to per-subject*, not removed. Aderyn cannot express that distinction. | +| L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** | Every file uses `pragma solidity ^0.8.20;`. The caret is intentional so the sources stay consumable as a library by projects on a different `0.8.x`; the compiler actually used for the deployed bytecode is pinned to `0.8.34` in `foundry.toml`, and `foundry.lock` pins every dependency. Verified: no file uses a construct that behaves differently across the allowed range. | +| L-3 | PUSH0 Opcode | Low | 9 | **Environment** | Consequence of `^0.8.20` plus `evm_version = prague`: the compiler emits `PUSH0`, which is unavailable on chains that have not adopted Shanghai. Not a source defect. A deployer targeting such a chain must lower `evm_version` in `foundry.toml` — but CMTAT v3 itself requires `prague`, so that configuration is out of scope for this engine. | +| L-4 | Loop Contains `require`/`revert` | Low | 4 | **By design** | `DocumentEngineBase.sol:124, 142, 156, 170` — the four batch loops. The reverts are raised inside `_setDocument` / `_removeDocument` (`ERC1643InvalidName`, `ERC1643InvalidSubject`, `ERC1643MissingDocument`). Batch operations are deliberately **all-or-nothing**: a batch containing one bad entry must not half-apply, since partial application would leave the operator unable to tell which documents were written without re-reading every entry. Skipping bad entries instead would silently drop them. | +| L-5 | Costly operations inside loop | Low | 5 | **By design** ×4, **known item** ×1 | Four instances (`:124, 142, 156, 170`) are storage writes in the batch loops — unavoidable, and the reason the batch functions exist is to amortise the 21 000-gas transaction overhead across those writes. The fifth (`:238`) is `_removeDocumentName`'s linear scan with swap-and-pop; see the triage note above and `ERC_RESULT.md` §4.7. | +| L-6 | Unchecked Return | Low | 1 | **False positive** | `DocumentEngine.sol:35`, `_grantRole(DEFAULT_ADMIN_ROLE, admin);`. OpenZeppelin's `_grantRole` returns `false` only when the account already holds the role. This call is in the constructor of a freshly deployed contract, where no role has been granted yet, so it always returns `true`; `admin == address(0)` is already rejected on the preceding lines. There is no state to check and no recovery path to take. | + +## Delta from the previous version + +None — this is the **first** static-analysis run recorded for this repository. `doc/audits/` did not +exist before `v0.4.0`; the `CHANGELOG.md` release checklist referenced `doc/audits/tools` but no +report had been committed. There is therefore no baseline to diff against, and future runs should +diff against this one. + +Note for the next run: the `CLAUDE.md` file tree claims a Slither report exists under `doc/`. It does +not. Slither is installed (`slither --version` resolves) and was **not** run for `v0.4.0` — this +release re-ran Aderyn only. A Slither run would make the next delta meaningful across both tools. diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md new file mode 100644 index 0000000..28a8414 --- /dev/null +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md @@ -0,0 +1,323 @@ +> **Summary — generated for DocumentEngine `v0.4.0` (CMTAT `v3.3.0-rc2`).** +> +> | | | +> | --- | --- | +> | Command | `aderyn -x mocks --output doc/audits/tools/v0.4.0/aderyn/aderyn-report.md` | +> | Tool version | `aderyn 0.6.5` | +> | Scope | `src/` only — 9 files, 298 nSLOC. **Mocks/tests excluded** (this project has no `src/mocks`; its mocks live in `test/`, which Aderyn does not scan). | +> | Result | **0 High · 6 Low · 0 Info** | +> | Verdict | **Nothing to fix.** No finding is exploitable. One (L-5 at `DocumentEngineBase.sol:238`) independently corroborates a known gas/scalability item already tracked as [`ERC_RESULT.md`](../../../../ERC_RESULT.md) §4.7. | +> +> | ID | Detector | Sev | Instances | Assessment | +> | --- | --- | --- | --- | --- | +> | L-1 | Centralization Risk | Low | 2 | **By design** — a document manager is a privileged operator by definition | +> | L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** — `^0.8.20` is deliberate; the deployed compiler is pinned in `foundry.toml` | +> | L-3 | PUSH0 Opcode | Low | 9 | **Environment** — `evm_version = prague`; only relevant on chains without PUSH0 | +> | L-4 | Loop Contains `require`/`revert` | Low | 4 | **By design** — batch operations are deliberately all-or-nothing | +> | L-5 | Costly operations inside loop | Low | 5 | **By design** (4 batch loops) + **1 known item** — `_removeDocumentName` is O(n), see §4.7 | +> | L-6 | Unchecked Return | Low | 1 | **False positive** — `_grantRole` in a constructor on a fresh contract cannot return `false` | +> +> Full triage, with the reasoning verified against each cited line: +> [`aderyn-report-feedback.md`](./aderyn-report-feedback.md). +> Security overview: [`doc/audits/AUDIT_OVERVIEW.md`](../../../AUDIT_OVERVIEW.md). + +# Aderyn Analysis Report + +This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a static analysis tool built by [Cyfrin](https://cyfrin.io), a blockchain security company. This report is not a substitute for manual audit or security review. It should not be relied upon for any purpose other than to assist in the identification of potential security vulnerabilities. +# Table of Contents + +- [Summary](#summary) + - [Files Summary](#files-summary) + - [Files Details](#files-details) + - [Issue Summary](#issue-summary) +- [Low Issues](#low-issues) + - [L-1: Centralization Risk](#l-1-centralization-risk) + - [L-2: Unspecific Solidity Pragma](#l-2-unspecific-solidity-pragma) + - [L-3: PUSH0 Opcode](#l-3-push0-opcode) + - [L-4: Loop Contains `require`/`revert`](#l-4-loop-contains-requirerevert) + - [L-5: Costly operations inside loop](#l-5-costly-operations-inside-loop) + - [L-6: Unchecked Return](#l-6-unchecked-return) + + +# Summary + +## Files Summary + +| Key | Value | +| --- | --- | +| .sol Files | 9 | +| Total nSLOC | 298 | + + +## Files Details + +| Filepath | nSLOC | +| --- | --- | +| src/DocumentEngine.sol | 52 | +| src/DocumentEngineBase.sol | 149 | +| src/DocumentEngineInvariant.sol | 6 | +| src/DocumentEngineOwnable.sol | 28 | +| src/interfaces/IERC1643MultiDocument.sol | 12 | +| src/interfaces/IERC8303.sol | 4 | +| src/interfaces/ITokenBinding.sol | 7 | +| src/modules/TokenBindingModule.sol | 28 | +| src/modules/VersionModule.sol | 12 | +| **Total** | **298** | + + +## Issue Summary + +| Category | No. of Issues | +| --- | --- | +| High | 0 | +| Low | 6 | + + +# Low Issues + +## L-1: Centralization Risk + +Contracts have owners with privileged rights to perform admin tasks and need to be trusted to not perform malicious updates or drain funds. + +
2 Found Instances + + +- Found in src/DocumentEngine.sol [Line: 24](../../../../../src/DocumentEngine.sol#L24) + + ```solidity + contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnumerable, ERC2771Context { + ``` + +- Found in src/DocumentEngineOwnable.sol [Line: 24](../../../../../src/DocumentEngineOwnable.sol#L24) + + ```solidity + contract DocumentEngineOwnable is TokenBindingModule, VersionModule, Ownable2Step, ERC2771Context { + ``` + +
+ + + +## L-2: Unspecific Solidity Pragma + +Consider using a specific version of Solidity in your contracts instead of a wide version. For example, instead of `pragma solidity ^0.8.0;`, use `pragma solidity 0.8.0;` + +
9 Found Instances + + +- Found in src/DocumentEngine.sol [Line: 2](../../../../../src/DocumentEngine.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/DocumentEngineBase.sol [Line: 2](../../../../../src/DocumentEngineBase.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/DocumentEngineInvariant.sol [Line: 2](../../../../../src/DocumentEngineInvariant.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/DocumentEngineOwnable.sol [Line: 2](../../../../../src/DocumentEngineOwnable.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/interfaces/IERC1643MultiDocument.sol [Line: 2](../../../../../src/interfaces/IERC1643MultiDocument.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/interfaces/IERC8303.sol [Line: 2](../../../../../src/interfaces/IERC8303.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/interfaces/ITokenBinding.sol [Line: 2](../../../../../src/interfaces/ITokenBinding.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/modules/TokenBindingModule.sol [Line: 2](../../../../../src/modules/TokenBindingModule.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/modules/VersionModule.sol [Line: 2](../../../../../src/modules/VersionModule.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +
+ + + +## L-3: PUSH0 Opcode + +Solc compiler version 0.8.20 switches the default target EVM version to Shanghai, which means that the generated bytecode will include PUSH0 opcodes. Be sure to select the appropriate EVM version in case you intend to deploy on a chain other than mainnet like L2 chains that may not support PUSH0, otherwise deployment of your contracts will fail. + +
9 Found Instances + + +- Found in src/DocumentEngine.sol [Line: 2](../../../../../src/DocumentEngine.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/DocumentEngineBase.sol [Line: 2](../../../../../src/DocumentEngineBase.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/DocumentEngineInvariant.sol [Line: 2](../../../../../src/DocumentEngineInvariant.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/DocumentEngineOwnable.sol [Line: 2](../../../../../src/DocumentEngineOwnable.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/interfaces/IERC1643MultiDocument.sol [Line: 2](../../../../../src/interfaces/IERC1643MultiDocument.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/interfaces/IERC8303.sol [Line: 2](../../../../../src/interfaces/IERC8303.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/interfaces/ITokenBinding.sol [Line: 2](../../../../../src/interfaces/ITokenBinding.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/modules/TokenBindingModule.sol [Line: 2](../../../../../src/modules/TokenBindingModule.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +- Found in src/modules/VersionModule.sol [Line: 2](../../../../../src/modules/VersionModule.sol#L2) + + ```solidity + pragma solidity ^0.8.20; + ``` + +
+ + + +## L-4: Loop Contains `require`/`revert` + +Avoid `require` / `revert` statements in a loop because a single bad item can cause the whole transaction to fail. It's better to forgive on fail and return failed elements post processing of the loop + +
4 Found Instances + + +- Found in src/DocumentEngineBase.sol [Line: 124](../../../../../src/DocumentEngineBase.sol#L124) + + ```solidity + for (uint256 i = 0; i < length; ++i) { + ``` + +- Found in src/DocumentEngineBase.sol [Line: 142](../../../../../src/DocumentEngineBase.sol#L142) + + ```solidity + for (uint256 i = 0; i < length; ++i) { + ``` + +- Found in src/DocumentEngineBase.sol [Line: 156](../../../../../src/DocumentEngineBase.sol#L156) + + ```solidity + for (uint256 i = 0; i < length; ++i) { + ``` + +- Found in src/DocumentEngineBase.sol [Line: 170](../../../../../src/DocumentEngineBase.sol#L170) + + ```solidity + for (uint256 i = 0; i < length; ++i) { + ``` + +
+ + + +## L-5: Costly operations inside loop + +Invoking `SSTORE` operations in loops may waste gas. Use a local variable to hold the loop computation result. + +
5 Found Instances + + +- Found in src/DocumentEngineBase.sol [Line: 124](../../../../../src/DocumentEngineBase.sol#L124) + + ```solidity + for (uint256 i = 0; i < length; ++i) { + ``` + +- Found in src/DocumentEngineBase.sol [Line: 142](../../../../../src/DocumentEngineBase.sol#L142) + + ```solidity + for (uint256 i = 0; i < length; ++i) { + ``` + +- Found in src/DocumentEngineBase.sol [Line: 156](../../../../../src/DocumentEngineBase.sol#L156) + + ```solidity + for (uint256 i = 0; i < length; ++i) { + ``` + +- Found in src/DocumentEngineBase.sol [Line: 170](../../../../../src/DocumentEngineBase.sol#L170) + + ```solidity + for (uint256 i = 0; i < length; ++i) { + ``` + +- Found in src/DocumentEngineBase.sol [Line: 238](../../../../../src/DocumentEngineBase.sol#L238) + + ```solidity + for (uint256 i = 0; i < length; ++i) { + ``` + +
+ + + +## L-6: Unchecked Return + +Function returns a value but it is ignored. Consider checking the return value. + +
1 Found Instances + + +- Found in src/DocumentEngine.sol [Line: 35](../../../../../src/DocumentEngine.sol#L35) + + ```solidity + _grantRole(DEFAULT_ADMIN_ROLE, admin); + ``` + +
+ + + diff --git a/foundry.lock b/foundry.lock index be96ac4..8dfec2c 100644 --- a/foundry.lock +++ b/foundry.lock @@ -1,20 +1,32 @@ { "lib/CMTAT": { - "rev": "e8048d43b0299afd83f150d3725ab299994b4271" + "tag": { + "name": "v3.3.0-rc2", + "rev": "35d8940b40943828c5ea407dc6b22d559d92e4ae" + } }, "lib/RuleEngine": { "tag": { - "name": "v2.1.0", - "rev": "461d32fca6cf501d6c15f3aed5f18855b2a6581c" + "name": "v3.0.0-rc4", + "rev": "66fcf2aafebd1f9d9de8a81dec92b88da071c9b3" } }, "lib/forge-std": { - "rev": "1714bee72e286e73f76e320d110e0eaf5c4e649d" + "tag": { + "name": "v1.7.1", + "rev": "f73c73d2018eb6a111f35e4dae7b4f27401e9421" + } }, "lib/openzeppelin-contracts": { - "rev": "dbb6104ce834628e473d2173bbc9d47f81a9eec3" + "tag": { + "name": "v5.6.1", + "rev": "5fd1781b1454fd1ef8e722282f86f9293cacf256" + } }, "lib/openzeppelin-contracts-upgradeable": { - "rev": "723f8cab09cdae1aca9ec9cc1cfa040c2d4b06c1" + "tag": { + "name": "v5.6.1", + "rev": "7bf4727aacdbfaa0f36cbd664654d0c9e1dc52bf" + } } -} \ No newline at end of file +} diff --git a/src/DocumentEngineBase.sol b/src/DocumentEngineBase.sol index 61f49a1..19ec715 100644 --- a/src/DocumentEngineBase.sol +++ b/src/DocumentEngineBase.sol @@ -174,15 +174,29 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen /** * @notice ERC-1643 function to get a document for the caller (`_msgSender()`) + * @dev Returns the three fields as flat values, matching the ERC-1643 ABI. The `Document` + * struct is kept for storage only: returning it would prepend a struct offset word to the + * returndata, so a consumer decoding per the ERC-1643 signature would silently mis-decode. */ - function getDocument(bytes32 name_) external view override returns (Document memory) { + function getDocument(bytes32 name_) + external + view + override + returns (string memory uri, bytes32 documentHash, uint256 lastModified) + { return _getDocument(_msgSender(), name_); } /** * @notice Public function to get a document for a specific contract address + * @dev Flat return, see {getDocument(bytes32)}. */ - function getDocument(address subject, bytes32 name_) external view override returns (Document memory) { + function getDocument(address subject, bytes32 name_) + external + view + override + returns (string memory uri, bytes32 documentHash, uint256 lastModified) + { return _getDocument(subject, name_); } @@ -205,10 +219,15 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen //////////////////////////////////////////////////////////////*/ /** - * @dev Internal function to fetch a document + * @dev Internal function to fetch a document, as flat values */ - function _getDocument(address subject, bytes32 name_) internal view returns (Document memory) { - return _documents[subject][name_]; + function _getDocument(address subject, bytes32 name_) + internal + view + returns (string memory uri, bytes32 documentHash, uint256 lastModified) + { + Document storage doc = _documents[subject][name_]; + return (doc.uri, doc.documentHash, doc.lastModified); } /** diff --git a/src/DocumentEngineInvariant.sol b/src/DocumentEngineInvariant.sol index d54d107..86f4469 100644 --- a/src/DocumentEngineInvariant.sol +++ b/src/DocumentEngineInvariant.sol @@ -14,15 +14,14 @@ abstract contract DocumentEngineInvariant { error InvalidInputLength(); error AdminWithAddressZeroNotAllowed(); - /// @notice Reverts when `setDocument` is called with `name == bytes32(0)`. - /// @dev ERC-1643-recommended error name. - error ERC1643InvalidName(); - /// @notice Reverts when a document is set for the null `subject` (`address(0)`). - /// @dev Multi-token extension guard; see IMP.md for the proposed standard note. + /// @dev Multi-token extension guard. The multi-subject draft names this condition + /// `MultiDocumentInvalidSubject()`; see + /// `doc/ERCSpecification/erc-draft_multi_document_management.md` and `ERC_RESULT.md` §4.4. error ERC1643InvalidSubject(); - /// @notice Reverts when `removeDocument` targets a document that does not exist. - /// @dev ERC-1643-recommended error name. - error ERC1643MissingDocument(); + // `ERC1643InvalidName()` and `ERC1643MissingDocument()` are NOT declared here: since + // CMTAT v3.3.0-rc2 they are declared by `IERC1643` itself, and the multi-subject draft + // requires a contract implementing both interfaces to obtain each error exactly once + // ("MUST NOT declare them twice"). Re-declaring them is a compile error. } diff --git a/src/interfaces/IERC1643MultiDocument.sol b/src/interfaces/IERC1643MultiDocument.sol index f0dbd42..16a98e5 100644 --- a/src/interfaces/IERC1643MultiDocument.sol +++ b/src/interfaces/IERC1643MultiDocument.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 pragma solidity ^0.8.20; -import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; - /** * @title IERC1643MultiDocument — optional multi-token ERC-1643 extension * @notice Address-scoped document management for a contract that manages @@ -12,11 +10,19 @@ import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; * being forced to implement the base single-argument functions. `subject` is the * address of the contract the documents belong to (typically a token contract, * but the reasoning applies to any ERC-721/ERC-1155 token, vault, or other - * on-chain product). See `doc/ERCSpecification/ERC-1643-proposition.md`. + * on-chain product). See `doc/ERCSpecification/erc-draft_multi_document_management.md`. */ interface IERC1643MultiDocument { /// @notice Returns metadata for the document `name` belonging to `subject`. - function getDocument(address subject, bytes32 name) external view returns (IERC1643.Document memory document); + /// @dev Returns the three fields as flat values, matching the specification ABI. A missing + /// document yields empty values (`""`, `bytes32(0)`, `0`) and does not revert. + /// @return uri Document location. + /// @return documentHash Hash of the document contents. + /// @return lastModified Last update timestamp. + function getDocument(address subject, bytes32 name) + external + view + returns (string memory uri, bytes32 documentHash, uint256 lastModified); /// @notice Returns all document names currently tracked for `subject`. function getAllDocuments(address subject) external view returns (bytes32[] memory documentNames); diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index 8a089af..59ac6b9 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -62,6 +62,24 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { bytes32 internal constant BASE_UPDATED_SIG = keccak256("DocumentUpdated(bytes32,string,bytes32)"); bytes32 internal constant BASE_REMOVED_SIG = keccak256("DocumentRemoved(bytes32,string,bytes32)"); + /** + * @dev Since CMTAT `v3.3.0-rc2`, `getDocument` returns the three ERC-1643 fields as flat + * values instead of a `Document` struct. These helpers repack them so the assertions below + * stay readable; {testGetDocumentReturnsFlatErc1643Abi} pins the wire format itself. + */ + function _doc(IERC1643MultiDocument engine_, address subject, bytes32 name_) + internal + view + returns (IERC1643.Document memory document) + { + (document.uri, document.documentHash, document.lastModified) = engine_.getDocument(subject, name_); + } + + /// @dev See {_doc(IERC1643MultiDocument,address,bytes32)}; caller-scoped ERC-1643 read. + function _doc(IERC1643 engine_, bytes32 name_) internal view returns (IERC1643.Document memory document) { + (document.uri, document.documentHash, document.lastModified) = engine_.getDocument(name_); + } + function setUp() public { documentEngine = new DocumentEngine(admin, AddressZero); vm.prank(admin); @@ -183,7 +201,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { assertEq(docs.length, 1); assertEq(docs[0], documentName); - IERC1643.Document memory doc = cmtat.getDocument(documentName); + IERC1643.Document memory doc = _doc(cmtat, documentName); assertEq(doc.uri, documentURI); assertEq(doc.documentHash, documentHash); assertEq(doc.lastModified, lastModif); @@ -207,7 +225,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { vm.prank(testContract); documentEngine.setDocument(selfName, selfURI, selfHash); - IERC1643.Document memory doc = documentEngine.getDocument(testContract, selfName); + IERC1643.Document memory doc = _doc(documentEngine, testContract, selfName); assertEq(doc.uri, selfURI); assertEq(doc.documentHash, selfHash); assertEq(doc.lastModified, block.timestamp); @@ -215,7 +233,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { // and can remove it vm.prank(testContract); documentEngine.removeDocument(selfName); - doc = documentEngine.getDocument(testContract, selfName); + doc = _doc(documentEngine, testContract, selfName); assertEq(doc.uri, ""); assertEq(doc.documentHash, ""); assertEq(doc.lastModified, 0); @@ -265,7 +283,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { vm.prank(attacker); openEngine.setDocument(testContract, documentName, documentURI, documentHash); - IERC1643.Document memory doc = openEngine.getDocument(testContract, documentName); + IERC1643.Document memory doc = _doc(openEngine, testContract, documentName); assertEq(doc.uri, documentURI); assertEq(doc.documentHash, documentHash); } @@ -289,6 +307,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { } function testSupportsERC1643Interfaces() public { + // Pinned literals: an interface id is the XOR of the selectors, which depend only on the + // function names and argument types. The CMTAT `v3.3.0-rc2` change of the `getDocument` + // return shape therefore moved neither id — which is exactly why that change was + // undetectable through ERC-165 (see {testGetDocumentReturnsFlatErc1643Abi}). + assertEq(type(IERC1643).interfaceId, bytes4(0xecfecec8)); + assertEq(type(IERC1643MultiDocument).interfaceId, bytes4(0xa2b1179b)); + // implements the base single-argument functions... assertTrue(documentEngine.supportsInterface(type(IERC1643).interfaceId)); // ...and the address-scoped multi-token extension @@ -297,19 +322,54 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { assertTrue(documentEngine.supportsInterface(type(ITokenBinding).interfaceId)); } + /** + * @dev Pins the `getDocument` wire format to the flat ERC-1643 ABI. + * + * Return types do not take part in a function signature, so returning a `Document` struct + * instead of the three flat values leaves both the selector and `type(IERC1643).interfaceId` + * unchanged: ERC-165 discovery cannot catch the difference, and a consumer built from the + * specification ABI would silently decode a struct return as garbage. The only way to catch a + * regression is to inspect the returndata, so assert the first word is the string offset + * (`0x60`) of a flat `(string,bytes32,uint256)` and not the `0x20` struct offset. + */ + function testGetDocumentReturnsFlatErc1643Abi() public { + (bool okSubject, bytes memory subjectScoped) = address(documentEngine) + .staticcall(abi.encodeWithSignature("getDocument(address,bytes32)", testContract, documentName)); + assertTrue(okSubject); + assertEq(_firstWord(subjectScoped), 0x60, "getDocument(address,bytes32) must return flat values"); + + vm.prank(testContract); + (bool okSelf, bytes memory selfScoped) = + address(documentEngine).staticcall(abi.encodeWithSignature("getDocument(bytes32)", documentName)); + assertTrue(okSelf); + assertEq(_firstWord(selfScoped), 0x60, "getDocument(bytes32) must return flat values"); + + // The decoded values must round-trip through the specification's own signature. + (string memory uri, bytes32 hash_, uint256 lastModified) = abi.decode(subjectScoped, (string, bytes32, uint256)); + assertEq(uri, documentURI); + assertEq(hash_, documentHash); + assertEq(lastModified, block.timestamp); + } + + function _firstWord(bytes memory data) private pure returns (uint256 word) { + assembly { + word := mload(add(data, 0x20)) + } + } + /*////////////////////////////////////////////////////////////// ERC-1643 input validation //////////////////////////////////////////////////////////////*/ function testCannotSetDocumentWithZeroName() public { vm.prank(admin); - vm.expectRevert(abi.encodeWithSelector(ERC1643InvalidName.selector)); + vm.expectRevert(abi.encodeWithSelector(IERC1643.ERC1643InvalidName.selector)); documentEngine.setDocument(testContract, bytes32(0), documentURI, documentHash); } function testCannotRemoveMissingDocument() public { vm.prank(admin); - vm.expectRevert(abi.encodeWithSelector(ERC1643MissingDocument.selector)); + vm.expectRevert(abi.encodeWithSelector(IERC1643.ERC1643MissingDocument.selector)); documentEngine.removeDocument(testContract, keccak256("does-not-exist")); } @@ -317,7 +377,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { vm.prank(admin); documentEngine.bindToken(testContract); vm.prank(testContract); - vm.expectRevert(abi.encodeWithSelector(ERC1643InvalidName.selector)); + vm.expectRevert(abi.encodeWithSelector(IERC1643.ERC1643InvalidName.selector)); documentEngine.setDocument(bytes32(0), documentURI, documentHash); } @@ -338,7 +398,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { vm.prank(admin); documentEngine.setDocument(testContract, documentName, documentURI, documentHash); - IERC1643.Document memory doc = documentEngine.getDocument(testContract, documentName); + IERC1643.Document memory doc = _doc(documentEngine, testContract, documentName); assertEq(doc.uri, documentURI); assertEq(doc.documentHash, documentHash); assertEq(doc.lastModified, lastModif); @@ -359,7 +419,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentEngine.setDocument(testContract, documentName, documentURIV2, documentHashV2); // Assert - IERC1643.Document memory doc = documentEngine.getDocument(testContract, documentName); + IERC1643.Document memory doc = _doc(documentEngine, testContract, documentName); assertEq(doc.uri, documentURIV2); assertEq(doc.documentHash, documentHashV2); assertEq(doc.lastModified, lastModif); @@ -389,13 +449,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentEngine.batchSetDocuments(smartContracts, names, uris, hashes); // Check the first document - IERC1643.Document memory doc1 = documentEngine.getDocument(testContract, documentName); + IERC1643.Document memory doc1 = _doc(documentEngine, testContract, documentName); assertEq(doc1.uri, documentURI); assertEq(doc1.documentHash, documentHash); assertEq(doc1.lastModified, block.timestamp); // Check the second document - IERC1643.Document memory doc2 = documentEngine.getDocument(anotherSmartContract, names[1]); + IERC1643.Document memory doc2 = _doc(documentEngine, anotherSmartContract, names[1]); assertEq(doc2.uri, uris[1]); assertEq(doc2.documentHash, hashes[1]); assertEq(doc2.lastModified, block.timestamp); @@ -422,13 +482,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentEngine.batchSetDocuments(smartContracts, names, uris, hashes); // Check the first document - IERC1643.Document memory doc1 = documentEngine.getDocument(testContract, documentName); + IERC1643.Document memory doc1 = _doc(documentEngine, testContract, documentName); assertEq(doc1.uri, documentURI); assertEq(doc1.documentHash, documentHash); assertEq(doc1.lastModified, block.timestamp); // Check the second document - IERC1643.Document memory doc2 = documentEngine.getDocument(testContract, names[1]); + IERC1643.Document memory doc2 = _doc(documentEngine, testContract, names[1]); assertEq(doc2.uri, uris[1]); assertEq(doc2.documentHash, hashes[1]); assertEq(doc2.lastModified, block.timestamp); @@ -498,7 +558,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { // Check that both documents are removed // Check the second document - IERC1643.Document memory doc = documentEngine.getDocument(testContract, documentName); + IERC1643.Document memory doc = _doc(documentEngine, testContract, documentName); assertEq(doc.uri, ""); assertEq(doc.documentHash, ""); assertEq(doc.lastModified, 0); @@ -524,14 +584,14 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { // Check that both documents are removed // Check the second document - IERC1643.Document memory doc = documentEngine.getDocument(testContract, documentName); + IERC1643.Document memory doc = _doc(documentEngine, testContract, documentName); assertEq(doc.uri, ""); assertEq(doc.documentHash, ""); assertEq(doc.lastModified, 0); bytes32[] memory docs = documentEngine.getAllDocuments(testContract); assertEq(docs.length, 0); - IERC1643.Document memory doc2 = documentEngine.getDocument(anotherSmartContract, names[1]); + IERC1643.Document memory doc2 = _doc(documentEngine, anotherSmartContract, names[1]); assertEq(doc2.uri, ""); assertEq(doc2.documentHash, ""); assertEq(doc2.lastModified, 0); @@ -580,13 +640,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentEngine.batchSetDocuments(testContract, names, uris, hashes); // Check the first document - IERC1643.Document memory doc1 = documentEngine.getDocument(testContract, documentName); + IERC1643.Document memory doc1 = _doc(documentEngine, testContract, documentName); assertEq(doc1.uri, documentURI); assertEq(doc1.documentHash, documentHash); assertEq(doc1.lastModified, block.timestamp); // Check the second document - IERC1643.Document memory doc2 = documentEngine.getDocument(testContract, names[1]); + IERC1643.Document memory doc2 = _doc(documentEngine, testContract, names[1]); assertEq(doc2.uri, uris[1]); assertEq(doc2.documentHash, hashes[1]); assertEq(doc2.lastModified, block.timestamp); @@ -606,13 +666,13 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { // Check that both documents are removed // Check the second document - IERC1643.Document memory doc = documentEngine.getDocument(testContract, documentName); + IERC1643.Document memory doc = _doc(documentEngine, testContract, documentName); assertEq(doc.uri, ""); assertEq(doc.documentHash, ""); assertEq(doc.lastModified, 0); bytes32[] memory docs = documentEngine.getAllDocuments(testContract); assertEq(docs.length, 0); - IERC1643.Document memory doc2 = documentEngine.getDocument(testContract, names[1]); + IERC1643.Document memory doc2 = _doc(documentEngine, testContract, names[1]); assertEq(doc2.uri, ""); assertEq(doc2.documentHash, ""); assertEq(doc2.lastModified, 0); @@ -677,7 +737,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { function testMsgSenderScopedReads() public { // setUp registered `documentName` for `testContract`; read it as that caller vm.prank(testContract); - IERC1643.Document memory doc = documentEngine.getDocument(documentName); + IERC1643.Document memory doc = _doc(documentEngine, documentName); assertEq(doc.uri, documentURI); assertEq(doc.documentHash, documentHash); @@ -690,7 +750,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { function testMsgSenderScopedReadReturnsEmptyForOther() public { // `attacker` has no documents of its own vm.prank(attacker); - IERC1643.Document memory doc = documentEngine.getDocument(documentName); + IERC1643.Document memory doc = _doc(documentEngine, documentName); assertEq(doc.uri, ""); assertEq(doc.documentHash, ""); assertEq(doc.lastModified, 0); @@ -735,7 +795,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { hashes[0] = documentHash; vm.prank(admin); - vm.expectRevert(abi.encodeWithSelector(ERC1643InvalidName.selector)); + vm.expectRevert(abi.encodeWithSelector(IERC1643.ERC1643InvalidName.selector)); documentEngine.batchSetDocuments(subjects, names, uris, hashes); } @@ -746,7 +806,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { names[0] = keccak256("never-set"); vm.prank(admin); - vm.expectRevert(abi.encodeWithSelector(ERC1643MissingDocument.selector)); + vm.expectRevert(abi.encodeWithSelector(IERC1643.ERC1643MissingDocument.selector)); documentEngine.batchRemoveDocuments(subjects, names); } @@ -789,7 +849,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { vm.prank(admin); documentEngine.setDocument(subject, name, uri, hash); - IERC1643.Document memory doc = documentEngine.getDocument(subject, name); + IERC1643.Document memory doc = _doc(documentEngine, subject, name); assertEq(doc.uri, uri); assertEq(doc.documentHash, hash); assertEq(doc.lastModified, block.timestamp); @@ -797,7 +857,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { vm.prank(admin); documentEngine.removeDocument(subject, name); - doc = documentEngine.getDocument(subject, name); + doc = _doc(documentEngine, subject, name); assertEq(doc.uri, ""); assertEq(doc.documentHash, ""); assertEq(doc.lastModified, 0); @@ -814,7 +874,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentEngine.setDocument(subjectA, name, documentURI, documentHash); // subjectB is unaffected - IERC1643.Document memory docB = documentEngine.getDocument(subjectB, name); + IERC1643.Document memory docB = _doc(documentEngine, subjectB, name); assertEq(docB.lastModified, 0); assertEq(documentEngine.getAllDocuments(subjectB).length, 0); } diff --git a/test/DocumentEngineOwnable.t.sol b/test/DocumentEngineOwnable.t.sol index 9e2caa8..b53e7ae 100644 --- a/test/DocumentEngineOwnable.t.sol +++ b/test/DocumentEngineOwnable.t.sol @@ -23,6 +23,19 @@ contract DocumentEngineOwnableTest is Test { bytes32 public documentHash = keccak256("doc1Hash"); address AddressZero = address(0); + /** + * @dev Since CMTAT `v3.3.0-rc2`, `getDocument` returns the three ERC-1643 fields as flat + * values instead of a `Document` struct; this helper repacks them so the assertions below + * stay readable. The wire format is pinned by `DocumentEngineTest`. + */ + function _doc(IERC1643MultiDocument engine_, address subject, bytes32 name_) + internal + view + returns (IERC1643.Document memory document) + { + (document.uri, document.documentHash, document.lastModified) = engine_.getDocument(subject, name_); + } + function setUp() public { engine = new DocumentEngineOwnable(owner, AddressZero); } @@ -44,14 +57,14 @@ contract DocumentEngineOwnableTest is Test { vm.prank(owner); engine.setDocument(testContract, documentName, documentURI, documentHash); - IERC1643.Document memory doc = engine.getDocument(testContract, documentName); + IERC1643.Document memory doc = _doc(engine, testContract, documentName); assertEq(doc.uri, documentURI); assertEq(doc.documentHash, documentHash); assertEq(doc.lastModified, block.timestamp); vm.prank(owner); engine.removeDocument(testContract, documentName); - doc = engine.getDocument(testContract, documentName); + doc = _doc(engine, testContract, documentName); assertEq(doc.lastModified, 0); } @@ -82,12 +95,12 @@ contract DocumentEngineOwnableTest is Test { vm.prank(testContract); engine.setDocument(documentName, documentURI, documentHash); - IERC1643.Document memory doc = engine.getDocument(testContract, documentName); + IERC1643.Document memory doc = _doc(engine, testContract, documentName); assertEq(doc.uri, documentURI); vm.prank(testContract); engine.removeDocument(documentName); - doc = engine.getDocument(testContract, documentName); + doc = _doc(engine, testContract, documentName); assertEq(doc.lastModified, 0); } From 5bcde9d1e6eb826ca677a99c09ad2304a34ff104 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Tue, 28 Jul 2026 15:56:56 +0200 Subject: [PATCH 22/47] refactor: move spec errors onto their interfaces, rename MultiDocumentInvalidSubject, guard token binding --- AGENTS.md | 24 ++++++-- CHANGELOG.md | 26 ++++++-- CLAUDE.md | 24 ++++++-- doc/audits/AUDIT_OVERVIEW.md | 4 +- .../v0.4.0/aderyn/aderyn-report-feedback.md | 15 ++++- .../tools/v0.4.0/aderyn/aderyn-report.md | 14 ++--- test/DocumentEngine.t.sol | 59 ++++++++++++++++++- 7 files changed, 137 insertions(+), 29 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 97f4dd0..094e91a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,6 +40,17 @@ addressed by a `bytes32` name. **not** the base `DocumentUpdated` / `DocumentRemoved` (those carry no address and are the token contract's responsibility). Extension declared in `src/interfaces/IERC1643MultiDocument.sol`; rationale in `ERC-1643-proposition.md`. +- **Errors live on interfaces, not on `DocumentEngineInvariant`.** Each specification + error is declared by the interface defining its condition — `ERC1643InvalidName` / + `ERC1643MissingDocument` on `IERC1643`, `MultiDocumentInvalidSubject` on + `IERC1643MultiDocument`, `TokenBindingInvalidToken` on `ITokenBinding` — so an ABI + generated from an interface carries its errors and each is obtained exactly once + (the multi-subject draft's "MUST NOT declare them twice", also a compile error). + `DocumentEngineInvariant` keeps only errors no interface defines. +- **Token binding is idempotent and rejects `address(0)`:** `bindToken` / `unbindToken` + write and emit `TokenBindingSet` only on an actual change, so every event is a real + transition; a repeat call succeeds silently. `address(0)` reverts + `TokenBindingInvalidToken()`. - **ERC-1643 conformance:** `setDocument` reverts `ERC1643InvalidName()` on `name == 0`; `removeDocument` reverts `ERC1643MissingDocument()` on a missing doc; `supportsInterface` advertises `IERC1643` + `IERC1643MultiDocument` (both deployments). @@ -81,16 +92,17 @@ src/ │ # supportsInterface, constructor ├── DocumentEngineOwnable.sol # Deployment #2: Ownable2Step (single owner) instead of │ # roles; document mgmt + binding are owner-only -├── DocumentEngineInvariant.sol # Shared errors only (InvalidInputLength, -│ # AdminWithAddressZeroNotAllowed, ERC1643InvalidSubject); -│ # ERC1643InvalidName / ERC1643MissingDocument come from -│ # IERC1643. NO access-control specifics +├── DocumentEngineInvariant.sol # Non-specification errors ONLY (InvalidInputLength, +│ # AdminWithAddressZeroNotAllowed). Every spec error is +│ # declared by its own interface — see the note below. +│ # NO access-control specifics ├── interfaces/ │ ├── IERC8303.sol # ERC-8303 "Contract Version" interface (id 0x54fd4d50) │ ├── IERC1643MultiDocument.sol # Multi-token ERC-1643 extension (address-scoped fns + -│ │ # DocumentUpdatedForSubject / DocumentRemovedForSubject) +│ │ # DocumentUpdatedForSubject / DocumentRemovedForSubject + +│ │ # MultiDocumentInvalidSubject) │ └── ITokenBinding.sol # Shared binding surface: bindToken / unbindToken / -│ # isTokenBound + TokenBindingSet (both deployments) +│ # isTokenBound + TokenBindingSet + TokenBindingInvalidToken └── modules/ ├── VersionModule.sol # Version module: implements ERC-8303 version() + ERC-165, │ # holds the VERSION constant (currently "0.4.0") diff --git a/CHANGELOG.md b/CHANGELOG.md index e62c7f0..2e3d094 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,9 +81,28 @@ for which CMTAT release each version of this engine is built against. CMTAT `v3.3.0-rc2` and are **not** re-declared here. The multi-subject draft requires a contract implementing both interfaces to obtain each error exactly once ("MUST NOT declare them twice"), and re-declaring is a compile error. Selectors, and hence revert data, are unchanged. - `ERC1643InvalidSubject()` stays local, since no interface defines it. + The same principle was applied to every other error: `MultiDocumentInvalidSubject()` moved to + `IERC1643MultiDocument` and `TokenBindingInvalidToken()` is declared on `ITokenBinding`, so an + ABI generated from an interface carries its errors. `DocumentEngineInvariant` now holds only + `InvalidInputLength` and `AdminWithAddressZeroNotAllowed`, which no interface defines. - Import path moved: `CMTAT/interfaces/engine/draft-IERC1643.sol` → `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. +- **`ERC1643InvalidSubject()` renamed to `MultiDocumentInvalidSubject()`** and moved from + `DocumentEngineInvariant` to `IERC1643MultiDocument`, matching the multi-subject draft. **This + changes the error selector**, so integrators decoding this revert must update. + + The draft's rule is that an error is prefixed by the proposal that *defines* its condition, not by + the one it sits next to. The null-`subject` condition cannot arise in ERC-1643 at all — its + `setDocument` has no `subject` argument, so the subject is implicitly the contract itself, which is + never the null address — so borrowing the `ERC1643` prefix named the error after a standard in + which it is unreachable. The two genuinely-shared errors keep their prefix for the opposite reason. +- **Token binding rejects `address(0)` and is idempotent.** `bindToken` / `unbindToken` now revert + `TokenBindingInvalidToken()` on the null address — which can never call the engine, so binding it + granted nothing while still emitting an event indexers key on — and write plus emit + `TokenBindingSet` **only when the binding actually changes**. A repeated call still succeeds, since + the caller's intent already holds, but emits nothing, so every event in the log is a real + transition and an indexer never has to de-duplicate. + ### Added - **Bound-token document management**: implement the now-mandatory `IERC1643.setDocument(name, uri, hash)` and `removeDocument(name)`, gated by the `onlyBoundToken` modifier and scoped to the caller (`_msgSender()`) own namespace. A token bound with `bindToken(token)` (see the shared binding module below) manages its own documents and can never affect another contract's documents. The admin overloads (explicit `address`, `DOCUMENT_MANAGER_ROLE`) are unchanged, so both systems work side by side. (RuleEngine's `ERC3643ComplianceExtendedModule` was evaluated for the binding but intentionally not reused — see the README.) @@ -104,7 +123,7 @@ Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/er - **Emission responsibility.** As a shared, multi-token manager the engine now emits **only** the address-carrying extension events and **no longer** emits the base `DocumentUpdated` / `DocumentRemoved` events (the spec's `MUST NOT` for a shared manager — those events carry no `subject` and belong on the token contract). - **Extension events/interface.** Renamed the multi-token events to the standard `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` (parameter `subject`), and introduced the `IERC1643MultiDocument` interface (`src/interfaces/IERC1643MultiDocument.sol`) that the base now implements — the address-scoped `getDocument` / `getAllDocuments` / `setDocument` / `removeDocument`. -- **Input validation.** `setDocument` now reverts `ERC1643InvalidName()` when `name == bytes32(0)` and `ERC1643InvalidSubject()` when `subject == address(0)` (the multi-token extension's null-namespace guard); `removeDocument` now reverts `ERC1643MissingDocument()` for a non-existent document (previously it silently emitted a spurious removal event). See [`erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md) for the corresponding multi-subject draft. +- **Input validation.** `setDocument` now reverts `ERC1643InvalidName()` when `name == bytes32(0)` and `MultiDocumentInvalidSubject()` when `subject == address(0)` (the multi-subject draft's null-namespace guard); `removeDocument` now reverts `ERC1643MissingDocument()` for a non-existent document (previously it silently emitted a spurious removal event). See [`erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md) for the corresponding multi-subject draft. - **ERC-165 discovery.** `supportsInterface` now returns `true` for `type(IERC1643).interfaceId` and `type(IERC1643MultiDocument).interfaceId` (both deployments). ### Added (token binding) @@ -122,8 +141,7 @@ Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/er straight to the engine has no execution point in the subject, so the subject emits nothing. See [`ERC_RESULT.md`](./ERC_RESULT.md) §4.3. - Open conformance gaps are tracked in [`ERC_RESULT.md`](./ERC_RESULT.md): per-`subject` - authorization (§4.2), the `ERC1643InvalidSubject` / `MultiDocumentInvalidSubject` naming - divergence (§4.4), and enumeration cost (§4.7). + authorization (§4.2), admin-path call topology (§4.3), and enumeration cost (§4.7). - CMTAT v3 no longer ships a *standalone* token that consumes an external document engine through its constructor; the standard token stores documents on-chain (`DocumentERC1643Module`). External-engine integration now goes through CMTAT's `DocumentEngineModule` (`setDocumentEngine`). The test suite was updated to exercise this real integration path via a minimal token built on `DocumentEngineModule`. ## v0.3.0 diff --git a/CLAUDE.md b/CLAUDE.md index 97f4dd0..094e91a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -40,6 +40,17 @@ addressed by a `bytes32` name. **not** the base `DocumentUpdated` / `DocumentRemoved` (those carry no address and are the token contract's responsibility). Extension declared in `src/interfaces/IERC1643MultiDocument.sol`; rationale in `ERC-1643-proposition.md`. +- **Errors live on interfaces, not on `DocumentEngineInvariant`.** Each specification + error is declared by the interface defining its condition — `ERC1643InvalidName` / + `ERC1643MissingDocument` on `IERC1643`, `MultiDocumentInvalidSubject` on + `IERC1643MultiDocument`, `TokenBindingInvalidToken` on `ITokenBinding` — so an ABI + generated from an interface carries its errors and each is obtained exactly once + (the multi-subject draft's "MUST NOT declare them twice", also a compile error). + `DocumentEngineInvariant` keeps only errors no interface defines. +- **Token binding is idempotent and rejects `address(0)`:** `bindToken` / `unbindToken` + write and emit `TokenBindingSet` only on an actual change, so every event is a real + transition; a repeat call succeeds silently. `address(0)` reverts + `TokenBindingInvalidToken()`. - **ERC-1643 conformance:** `setDocument` reverts `ERC1643InvalidName()` on `name == 0`; `removeDocument` reverts `ERC1643MissingDocument()` on a missing doc; `supportsInterface` advertises `IERC1643` + `IERC1643MultiDocument` (both deployments). @@ -81,16 +92,17 @@ src/ │ # supportsInterface, constructor ├── DocumentEngineOwnable.sol # Deployment #2: Ownable2Step (single owner) instead of │ # roles; document mgmt + binding are owner-only -├── DocumentEngineInvariant.sol # Shared errors only (InvalidInputLength, -│ # AdminWithAddressZeroNotAllowed, ERC1643InvalidSubject); -│ # ERC1643InvalidName / ERC1643MissingDocument come from -│ # IERC1643. NO access-control specifics +├── DocumentEngineInvariant.sol # Non-specification errors ONLY (InvalidInputLength, +│ # AdminWithAddressZeroNotAllowed). Every spec error is +│ # declared by its own interface — see the note below. +│ # NO access-control specifics ├── interfaces/ │ ├── IERC8303.sol # ERC-8303 "Contract Version" interface (id 0x54fd4d50) │ ├── IERC1643MultiDocument.sol # Multi-token ERC-1643 extension (address-scoped fns + -│ │ # DocumentUpdatedForSubject / DocumentRemovedForSubject) +│ │ # DocumentUpdatedForSubject / DocumentRemovedForSubject + +│ │ # MultiDocumentInvalidSubject) │ └── ITokenBinding.sol # Shared binding surface: bindToken / unbindToken / -│ # isTokenBound + TokenBindingSet (both deployments) +│ # isTokenBound + TokenBindingSet + TokenBindingInvalidToken └── modules/ ├── VersionModule.sol # Version module: implements ERC-8303 version() + ERC-165, │ # holds the VERSION constant (currently "0.4.0") diff --git a/doc/audits/AUDIT_OVERVIEW.md b/doc/audits/AUDIT_OVERVIEW.md index 1aab618..5955311 100644 --- a/doc/audits/AUDIT_OVERVIEW.md +++ b/doc/audits/AUDIT_OVERVIEW.md @@ -46,6 +46,8 @@ static analyzers — neither tool can see these, since both are ABI- and specifi | --- | --- | --- | | `getDocument` returned a `Document` struct where ERC-1643 mandates three flat values. Same selector and same `type(IERC1643).interfaceId` either way, so ERC-165 detection could not distinguish them and a spec-conformant consumer silently decoded corrupt values | High | **Fixed** — flat return on both overloads, pinned by `testGetDocumentReturnsFlatErc1643Abi`, which inspects the returndata directly | | `ERC1643InvalidName` / `ERC1643MissingDocument` declared both locally and by `IERC1643`, which the multi-subject draft forbids and the compiler rejects | Blocker | **Fixed** — local declarations removed | +| Null-subject error named `ERC1643InvalidSubject`, after a standard in which the condition cannot occur, and declared on an abstract contract rather than an interface | Low | **Fixed** — renamed `MultiDocumentInvalidSubject` and moved to `IERC1643MultiDocument`; every specification error now sits on the interface defining its condition | +| `bindToken(address(0))` accepted, and bind/unbind emitted `TokenBindingSet` even when the binding did not change | Low | **Fixed** — null address rejected with `TokenBindingInvalidToken()`; both are now idempotent and emit only on a real transition | ## Known open items @@ -56,9 +58,7 @@ detail in [`ERC_RESULT.md`](../../ERC_RESULT.md) §7. | --- | --- | --- | | Authorization is not per-`subject`, and `_authorizeDocumentManagement()` takes no `subject`, so a deployment cannot make it per-subject by overriding the hook | High | §4.2 | | Admin write path has no execution point in the subject, so an ERC-1643 subject emits nothing for writes sent straight to the engine | Medium | §4.3 | -| `ERC1643InvalidSubject` vs the draft's `MultiDocumentInvalidSubject` naming | Low | §4.4 | | `_removeDocumentName` is O(n); no paginated enumeration | Low | §4.7 — also surfaced by Aderyn L-5 | -| `bindToken(address(0))` accepted; bind/unbind emit unconditionally | Low | §4.8 | | The ERC-2771 trusted forwarder can act as any bound subject and is immutable | Info | §4.9 | ## Reporting a vulnerability diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md index 4008de5..bb14b07 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md @@ -5,7 +5,7 @@ | Report | [`aderyn-report.md`](./aderyn-report.md) | | Command | `aderyn -x mocks --output doc/audits/tools/v0.4.0/aderyn/aderyn-report.md` | | Tool version | `aderyn 0.6.5` | -| Scope | `src/` only — 9 files, 298 nSLOC. **Mocks and tests excluded.** This project keeps its mocks (`CMTATDocumentEngineMock`, `OpenDocumentEngine`) inside `test/DocumentEngine.t.sol`, which Aderyn does not scan, so `-x mocks` matched nothing and changed nothing. | +| Scope | `src/` only — 9 files, 307 nSLOC. **Mocks and tests excluded.** This project keeps its mocks (`CMTATDocumentEngineMock`, `OpenDocumentEngine`) inside `test/DocumentEngine.t.sol`, which Aderyn does not scan, so `-x mocks` matched nothing and changed nothing. | | Dependency | CMTAT `v3.3.0-rc2` (`35d8940b`) | | Result | **0 High · 6 Low** | @@ -33,10 +33,21 @@ CMTAT upgrade. | L-1 | Centralization Risk | Low | 2 | **By design** | `DocumentEngine.sol:24`, `DocumentEngineOwnable.sol:24`. The whole premise of the contract is that a trusted operator manages documents for a fleet of subjects; `DOCUMENT_MANAGER_ROLE` (and `owner`) are that operator. Documented in the README and analysed in `ERC_RESULT.md` §4.2, which argues the privilege should be *narrowed to per-subject*, not removed. Aderyn cannot express that distinction. | | L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** | Every file uses `pragma solidity ^0.8.20;`. The caret is intentional so the sources stay consumable as a library by projects on a different `0.8.x`; the compiler actually used for the deployed bytecode is pinned to `0.8.34` in `foundry.toml`, and `foundry.lock` pins every dependency. Verified: no file uses a construct that behaves differently across the allowed range. | | L-3 | PUSH0 Opcode | Low | 9 | **Environment** | Consequence of `^0.8.20` plus `evm_version = prague`: the compiler emits `PUSH0`, which is unavailable on chains that have not adopted Shanghai. Not a source defect. A deployer targeting such a chain must lower `evm_version` in `foundry.toml` — but CMTAT v3 itself requires `prague`, so that configuration is out of scope for this engine. | -| L-4 | Loop Contains `require`/`revert` | Low | 4 | **By design** | `DocumentEngineBase.sol:124, 142, 156, 170` — the four batch loops. The reverts are raised inside `_setDocument` / `_removeDocument` (`ERC1643InvalidName`, `ERC1643InvalidSubject`, `ERC1643MissingDocument`). Batch operations are deliberately **all-or-nothing**: a batch containing one bad entry must not half-apply, since partial application would leave the operator unable to tell which documents were written without re-reading every entry. Skipping bad entries instead would silently drop them. | +| L-4 | Loop Contains `require`/`revert` | Low | 4 | **By design** | `DocumentEngineBase.sol:124, 142, 156, 170` — the four batch loops. The reverts are raised inside `_setDocument` / `_removeDocument` (`ERC1643InvalidName`, `MultiDocumentInvalidSubject`, `ERC1643MissingDocument`). Batch operations are deliberately **all-or-nothing**: a batch containing one bad entry must not half-apply, since partial application would leave the operator unable to tell which documents were written without re-reading every entry. Skipping bad entries instead would silently drop them. | | L-5 | Costly operations inside loop | Low | 5 | **By design** ×4, **known item** ×1 | Four instances (`:124, 142, 156, 170`) are storage writes in the batch loops — unavoidable, and the reason the batch functions exist is to amortise the 21 000-gas transaction overhead across those writes. The fifth (`:238`) is `_removeDocumentName`'s linear scan with swap-and-pop; see the triage note above and `ERC_RESULT.md` §4.7. | | L-6 | Unchecked Return | Low | 1 | **False positive** | `DocumentEngine.sol:35`, `_grantRole(DEFAULT_ADMIN_ROLE, admin);`. OpenZeppelin's `_grantRole` returns `false` only when the account already holds the role. This call is in the constructor of a freshly deployed contract, where no role has been granted yet, so it always returns `true`; `admin == address(0)` is already rejected on the preceding lines. There is no state to check and no recovery path to take. | +## Delta + +This report was regenerated after the `ERC_RESULT.md` §7 items 3 and 6 fixes (error renaming and +relocation; `bindToken`/`unbindToken` null-address rejection and idempotence). **Nothing moved**: +the same six detectors fire with the same instance counts, and every cited line is unchanged. nSLOC +rose 298 → 307 for the added guard and its NatSpec. + +Worth noting explicitly, since it is a null result that is easy to misread as "not analysed": the new +`TokenBindingModule._setTokenBinding` — which adds a revert and an early return — triggered **no** +new finding, including no addition to L-4 (`revert` in a loop), because it contains no loop. + ## Delta from the previous version None — this is the **first** static-analysis run recorded for this repository. `doc/audits/` did not diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md index 28a8414..13917cd 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md @@ -4,7 +4,7 @@ > | --- | --- | > | Command | `aderyn -x mocks --output doc/audits/tools/v0.4.0/aderyn/aderyn-report.md` | > | Tool version | `aderyn 0.6.5` | -> | Scope | `src/` only — 9 files, 298 nSLOC. **Mocks/tests excluded** (this project has no `src/mocks`; its mocks live in `test/`, which Aderyn does not scan). | +> | Scope | `src/` only — 9 files, 307 nSLOC. **Mocks/tests excluded** (this project has no `src/mocks`; its mocks live in `test/`, which Aderyn does not scan). | > | Result | **0 High · 6 Low · 0 Info** | > | Verdict | **Nothing to fix.** No finding is exploitable. One (L-5 at `DocumentEngineBase.sol:238`) independently corroborates a known gas/scalability item already tracked as [`ERC_RESULT.md`](../../../../ERC_RESULT.md) §4.7. | > @@ -46,7 +46,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Key | Value | | --- | --- | | .sol Files | 9 | -| Total nSLOC | 298 | +| Total nSLOC | 307 | ## Files Details @@ -55,14 +55,14 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | --- | --- | | src/DocumentEngine.sol | 52 | | src/DocumentEngineBase.sol | 149 | -| src/DocumentEngineInvariant.sol | 6 | +| src/DocumentEngineInvariant.sol | 5 | | src/DocumentEngineOwnable.sol | 28 | -| src/interfaces/IERC1643MultiDocument.sol | 12 | +| src/interfaces/IERC1643MultiDocument.sol | 13 | | src/interfaces/IERC8303.sol | 4 | -| src/interfaces/ITokenBinding.sol | 7 | -| src/modules/TokenBindingModule.sol | 28 | +| src/interfaces/ITokenBinding.sol | 8 | +| src/modules/TokenBindingModule.sol | 36 | | src/modules/VersionModule.sol | 12 | -| **Total** | **298** | +| **Total** | **307** | ## Issue Summary diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index 59ac6b9..9ea7d81 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -58,6 +58,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { // Local copies of the extension events, so `vm.expectEmit` can emit and match them. event DocumentUpdatedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); event DocumentRemovedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); + event TokenBindingSet(address indexed token, bool bound); // Base ERC-1643 event signatures (this shared engine must NOT emit them). bytes32 internal constant BASE_UPDATED_SIG = keccak256("DocumentUpdated(bytes32,string,bytes32)"); bytes32 internal constant BASE_REMOVED_SIG = keccak256("DocumentRemoved(bytes32,string,bytes32)"); @@ -257,6 +258,60 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { assertFalse(documentEngine.isTokenBound(testContract)); } + function testCannotBindZeroAddress() public { + vm.prank(admin); + vm.expectRevert(abi.encodeWithSelector(ITokenBinding.TokenBindingInvalidToken.selector)); + documentEngine.bindToken(AddressZero); + + vm.prank(admin); + vm.expectRevert(abi.encodeWithSelector(ITokenBinding.TokenBindingInvalidToken.selector)); + documentEngine.unbindToken(AddressZero); + + assertFalse(documentEngine.isTokenBound(AddressZero)); + } + + /** + * @dev Binding is idempotent: the repeated call succeeds, because the caller's intent already + * holds, but emits nothing — so every {TokenBindingSet} in the log is a real transition and an + * indexer never has to de-duplicate. + */ + function testBindTokenIsIdempotentAndDoesNotReEmit() public { + vm.prank(admin); + vm.expectEmit(true, false, false, true); + emit TokenBindingSet(testContract, true); + documentEngine.bindToken(testContract); + + // second bind: succeeds, changes nothing, emits nothing + vm.recordLogs(); + vm.prank(admin); + documentEngine.bindToken(testContract); + assertEq(vm.getRecordedLogs().length, 0, "re-binding must not emit"); + assertTrue(documentEngine.isTokenBound(testContract)); + } + + function testUnbindTokenIsIdempotentAndDoesNotReEmit() public { + // unbinding a token that was never bound: succeeds, emits nothing + vm.recordLogs(); + vm.prank(admin); + documentEngine.unbindToken(testContract); + assertEq(vm.getRecordedLogs().length, 0, "unbinding an unbound token must not emit"); + assertFalse(documentEngine.isTokenBound(testContract)); + + vm.prank(admin); + documentEngine.bindToken(testContract); + + vm.prank(admin); + vm.expectEmit(true, false, false, true); + emit TokenBindingSet(testContract, false); + documentEngine.unbindToken(testContract); + + vm.recordLogs(); + vm.prank(admin); + documentEngine.unbindToken(testContract); + assertEq(vm.getRecordedLogs().length, 0, "re-unbinding must not emit"); + assertFalse(documentEngine.isTokenBound(testContract)); + } + function testUnboundContractCannotSetOwnDocument() public { bytes32 selfName = keccak256("self-doc"); vm.prank(attacker); @@ -765,7 +820,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { function testCannotSetDocumentForZeroSubject() public { vm.prank(admin); - vm.expectRevert(abi.encodeWithSelector(ERC1643InvalidSubject.selector)); + vm.expectRevert(abi.encodeWithSelector(IERC1643MultiDocument.MultiDocumentInvalidSubject.selector)); documentEngine.setDocument(AddressZero, documentName, documentURI, documentHash); } @@ -780,7 +835,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { hashes[0] = documentHash; vm.prank(admin); - vm.expectRevert(abi.encodeWithSelector(ERC1643InvalidSubject.selector)); + vm.expectRevert(abi.encodeWithSelector(IERC1643MultiDocument.MultiDocumentInvalidSubject.selector)); documentEngine.batchSetDocuments(subjects, names, uris, hashes); } From 505905947efb69d11086d9ea078af1cdbf816d70 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Tue, 28 Jul 2026 17:01:52 +0200 Subject: [PATCH 23/47] fix: move spec errors onto their interfaces, guard token binding, add IMPROVEMENT.md --- AGENTS.md | 8 +- CHANGELOG.md | 6 +- CLAUDE.md | 8 +- IMPROVEMENT.md | 269 ++++++++++++++++++ doc/audits/AUDIT_OVERVIEW.md | 15 +- .../v0.4.0/aderyn/aderyn-report-feedback.md | 8 +- .../tools/v0.4.0/aderyn/aderyn-report.md | 2 +- src/DocumentEngineBase.sol | 2 +- src/DocumentEngineInvariant.sol | 18 +- src/interfaces/IERC1643MultiDocument.sol | 9 + src/interfaces/ITokenBinding.sol | 9 + src/modules/TokenBindingModule.sol | 29 +- 12 files changed, 347 insertions(+), 36 deletions(-) create mode 100644 IMPROVEMENT.md diff --git a/AGENTS.md b/AGENTS.md index 094e91a..95b69ad 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -62,7 +62,7 @@ addressed by a `bytes32` name. A struct return prepends an offset word to the returndata while leaving the selector and `type(IERC1643).interfaceId` unchanged, so the mismatch is invisible to ERC-165 and a spec-conformant consumer silently mis-decodes. Pinned by - `testGetDocumentReturnsFlatErc1643Abi`; see `ERC_RESULT.md` §4.1. + `testGetDocumentReturnsFlatErc1643Abi`. - **ERC-2771:** meta-transaction (gasless) support; `_msgSender()` is used everywhere. - **Access control:** `DEFAULT_ADMIN_ROLE` implicitly has every role (see the `hasRole` override). @@ -145,8 +145,10 @@ Other important files: static-analysis output under `doc/audits/tools/vX.Y.Z//`, each with a `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging every finding. Aderyn was run for `v0.4.0`; Slither has never been run here. -- `ERC_RESULT.md` — conformance analysis against both ERC specifications; the - open items live in its §7. +- `IMPROVEMENT.md` — the open items: deviations from the two ERC specifications, + with severity, effort and a recommendation for each. Update it when an item is + fixed (move the record to `CHANGELOG.md` and `doc/audits/AUDIT_OVERVIEW.md`) + or when review surfaces a new one. - `lib/` — submodules: `CMTAT`, `RuleEngine`, `openzeppelin-contracts(-upgradeable)`, `forge-std`. ## Dependencies (tested versions) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e3d094..c720fe7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -139,9 +139,9 @@ Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/er subject-initiated call topology is fully conformant with the multi-subject draft's *Emission Responsibility* rules. The **admin path remains non-conformant by construction** — a write sent straight to the engine has no execution point in the subject, so the subject emits nothing. - See [`ERC_RESULT.md`](./ERC_RESULT.md) §4.3. -- Open conformance gaps are tracked in [`ERC_RESULT.md`](./ERC_RESULT.md): per-`subject` - authorization (§4.2), admin-path call topology (§4.3), and enumeration cost (§4.7). + See [`IMPROVEMENT.md`](./IMPROVEMENT.md) item 2. +- Open conformance gaps are tracked in [`IMPROVEMENT.md`](./IMPROVEMENT.md): per-`subject` + authorization (item 1), admin-path call topology (item 2), and enumeration cost (item 4). - CMTAT v3 no longer ships a *standalone* token that consumes an external document engine through its constructor; the standard token stores documents on-chain (`DocumentERC1643Module`). External-engine integration now goes through CMTAT's `DocumentEngineModule` (`setDocumentEngine`). The test suite was updated to exercise this real integration path via a minimal token built on `DocumentEngineModule`. ## v0.3.0 diff --git a/CLAUDE.md b/CLAUDE.md index 094e91a..95b69ad 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -62,7 +62,7 @@ addressed by a `bytes32` name. A struct return prepends an offset word to the returndata while leaving the selector and `type(IERC1643).interfaceId` unchanged, so the mismatch is invisible to ERC-165 and a spec-conformant consumer silently mis-decodes. Pinned by - `testGetDocumentReturnsFlatErc1643Abi`; see `ERC_RESULT.md` §4.1. + `testGetDocumentReturnsFlatErc1643Abi`. - **ERC-2771:** meta-transaction (gasless) support; `_msgSender()` is used everywhere. - **Access control:** `DEFAULT_ADMIN_ROLE` implicitly has every role (see the `hasRole` override). @@ -145,8 +145,10 @@ Other important files: static-analysis output under `doc/audits/tools/vX.Y.Z//`, each with a `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging every finding. Aderyn was run for `v0.4.0`; Slither has never been run here. -- `ERC_RESULT.md` — conformance analysis against both ERC specifications; the - open items live in its §7. +- `IMPROVEMENT.md` — the open items: deviations from the two ERC specifications, + with severity, effort and a recommendation for each. Update it when an item is + fixed (move the record to `CHANGELOG.md` and `doc/audits/AUDIT_OVERVIEW.md`) + or when review surfaces a new one. - `lib/` — submodules: `CMTAT`, `RuleEngine`, `openzeppelin-contracts(-upgradeable)`, `forge-std`. ## Dependencies (tested versions) diff --git a/IMPROVEMENT.md b/IMPROVEMENT.md new file mode 100644 index 0000000..cf44cda --- /dev/null +++ b/IMPROVEMENT.md @@ -0,0 +1,269 @@ +# IMPROVEMENT — open items + +Known deviations, gaps and improvement opportunities in `DocumentEngine`, carried forward as of +**`v0.4.0`** (CMTAT `v3.3.0-rc2`). + +They come from a clause-by-clause conformance analysis of the implementation against the two +specifications this engine implements: + +| Spec | File | Role | +| --- | --- | --- | +| ERC-1643 — Document Management for Security Tokens | [`doc/ERCSpecification/erc-1643.md`](./doc/ERCSpecification/erc-1643.md) | Per-contract interface | +| Multi-Subject Document Management (unnumbered draft) | [`doc/ERCSpecification/erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md) | Address-scoped companion interface | + +**None of these is an exploitable vulnerability.** Items already fixed are not repeated here — they +are recorded in [`CHANGELOG.md`](./CHANGELOG.md) and in +[`doc/audits/AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md). Static-analysis findings are +tracked separately under [`doc/audits/tools/`](./doc/audits/tools). + +> This project has not been audited. These items are the output of specification review and +> automated tooling, not of a formal security audit. + +## Summary + +| # | Item | Severity | Effort | Kind | +| --- | --- | --- | --- | --- | +| [1](#1--authorization-is-not-per-subject-and-the-hook-cannot-express-it) | Authorization is not per-`subject`, and the hook cannot express it | **High** | Medium | Spec `MUST` | +| [2](#2--the-admin-path-bypasses-subject-side-erc-1643-emission) | Admin path bypasses subject-side ERC-1643 emission | **Medium** | Small–Medium | Spec `SHOULD` | +| [3](#3--the-engine-advertises-ierc1643-but-is-not-a-usable-erc-1643-endpoint) | Engine advertises `IERC1643` but is not a usable ERC-1643 endpoint | Low | Trivial | Docs | +| [4](#4--enumeration-cost-and-removal-complexity) | Enumeration cost and removal complexity | Low | Medium | Gas | +| [5](#5--the-erc-2771-forwarder-is-a-universal-write-authority) | ERC-2771 forwarder is a universal write authority | Info | Trivial | Docs | +| [6](#6--_removedocument-emits-before-the-state-change) | `_removeDocument` emits before the state change | Info | Trivial | Cosmetic | +| [7](#7--upstream-imultidocumentsubject-manager-discovery) | Upstream: `IMultiDocumentSubject` manager discovery | Info | — | Upstream | + +Item 1 is the only one that changes what the contract can express; everything else is documentation, +gas, or cosmetic. Item 1 is also **source-compatible** for existing deployments — the default hook +bodies would ignore the new argument — so it does not have to wait for a breaking release. + +--- + +## 1 — Authorization is not per-`subject`, and the hook cannot express it + +**Severity:** High · **Effort:** Medium · **Kind:** deviation from a specification `MUST` + +**Where:** `src/DocumentEngineBase.sol:56`, `:71-84`, `:111-186`; `src/DocumentEngine.sol:46-48`; +`src/DocumentEngineOwnable.sol:39-41` + +> Implementations **MUST** authorize writes per `subject`, so that a caller cannot create, update, or +> remove documents for a `subject` it is not permitted to manage. +> — draft §Authorization + +`DOCUMENT_MANAGER_ROLE` is a single global permission: every holder may write for *every* subject. +`DocumentEngineOwnable` is the same with `owner`. This is precisely the risk the draft's Security +Considerations open with — "a management contract holds the document sets of unrelated subjects +behind a single address" — and the engine's design is, by default, the unmitigated case. + +That alone might be defensible as a deployment choice, since the project's stated model is a single +operator managing a fleet it owns. The structural problem is that the escape hatch does not work: + +```solidity +function _authorizeDocumentManagement() internal view virtual; // no subject parameter +``` + +A deployment cannot override this hook to implement per-subject rules, because at the point it runs +the subject is not available. In the batch functions the situation is worse — the modifier fires +**once** for the whole call, before any element is read, so even a subject-aware hook would be +bypassed for `batchSetDocuments` / `batchRemoveDocuments`. + +Consequently the draft's own test case — + +> A caller not authorized for a subject failing to create, update, or remove that subject's documents. + +— is untestable against this codebase, and is indeed absent from the suite. The existing +`testNonAdmin*` tests cover the globally-unauthorized case only. + +**Mitigating factor.** The **bound-token path is already per-subject** and cannot be escaped: the +namespace is `_msgSender()`, structurally. A deployment that overrides +`_authorizeDocumentManagement()` to always revert is fully conformant today — but that removes the +admin path entirely rather than scoping it. + +**Recommendation.** Change the hook signature and call it per subject: + +```solidity +function _authorizeDocumentManagement(address subject) internal view virtual; + +function batchSetDocuments(address[] calldata subjects, ...) external { + for (uint256 i = 0; i < length; ++i) { + _authorizeDocumentManagement(subjects[i]); + _setDocument(subjects[i], names[i], uris[i], hashes[i]); + } +} +``` + +The default implementations stay exactly as they are (`_checkRole(DOCUMENT_MANAGER_ROLE)` / +`_checkOwner()`, ignoring `subject`), so behaviour and gas are effectively unchanged, but a +deployment gains the ability to be conformant — for example a per-subject role +`keccak256("DOCUMENT_MANAGER", subject)`. Token binding needs a separate hook +(`_authorizeTokenBinding()`), since binding has no subject. + +Add the draft's missing test case once the hook can express it. + +## 2 — The admin path bypasses subject-side ERC-1643 emission + +**Severity:** Medium · **Effort:** Small–Medium · **Kind:** deviation from a specification `SHOULD` + +**Where:** `src/DocumentEngineBase.sol:71-84`; demonstrated at `test/DocumentEngine.t.sol:197` + +> A deployment in which an operator calls `setDocument(address subject, ...)` … directly, with no +> execution point in the subject, does **not** satisfy ERC-1643's emission requirement for that +> subject. +> — draft §Call Topology + +> a management contract SHOULD restrict its address-scoped writes to callers for which one of the two +> topologies above holds. + +Neither deployment applies such a restriction. The repository's own CMTAT integration test writes +through exactly the prohibited path: + +```solidity +// test/DocumentEngine.t.sol:196-197 +vm.prank(admin); +documentEngine.setDocument(address(cmtat), documentName, documentURI, documentHash); +``` + +The engine emits `DocumentUpdatedForSubject`; the CMTAT token emits nothing; anyone subscribed to the +token's address concludes its documents are unchanged. The failure is silent in both directions, as +the draft's Security Considerations describe. + +The **subject-initiated** topology, by contrast, became fully conformant with CMTAT `v3.3.0-rc2`, +which lists this under *Fixed* as "Delegating document token emits ERC-1643 events on its own address +(dual emission)". `DocumentEngineModule` forwards to the engine and then re-emits `DocumentUpdated` / +`DocumentRemoved` on the **token's** own address (`DocumentEngineModule.sol:91-92`, `:102-104`), +reading the metadata before removal so `DocumentRemoved` carries the removed values as the spec +requires; the engine emits the address-carrying events on its own address. Both mutators also revert +with `CMTAT_DocumentEngineModule_NoDocumentEngine` when no engine is set, closing a path where a +write would previously have been lost. + +So the gap is narrow but sharp: whether a CMTAT subject's documents are observable on its own address +depends entirely on **which door the operator uses**. Through the token (`cmtat.setDocument(...)`) it +is; straight to the engine (`engine.setDocument(address(cmtat), ...)`) it is not. Nothing in either +contract signals the difference. + +**Recommendation.** Pick one and state it: + +- *Documentation-only* (cheapest): a prominent README/NatSpec note that the address-scoped writes are + for subjects that either do not implement ERC-1643 or accept the loss of per-contract + observability; the bound-token path is the conformant route for ERC-1643 subjects. +- *Enforced*: gate the address-scoped writes on `isTokenBound(subject) == false`, forcing ERC-1643 + subjects through their own contract. +- *Callback*: add the draft's "manager-initiated with callback" topology — an optional permissioned + hook on the subject invoked after the write, so the subject emits. + +## 3 — The engine advertises `IERC1643` but is not a usable ERC-1643 endpoint + +**Severity:** Low · **Effort:** Trivial · **Kind:** documentation + +**Where:** `src/DocumentEngine.sol:88`; `src/DocumentEngineOwnable.sol:47-49` + +This is permitted — the draft's rule is that a contract may advertise `type(IERC1643).interfaceId` +*only if* it implements the base functions, and the engine does. But those functions are +`_msgSender()`-scoped (`DocumentEngineBase.sol:181-216`), so a consumer that ERC-165-detects +ERC-1643 on the **engine** address and then calls `getDocument(name)` receives empty values, no +revert, and never sees a base event. This is the deployment error the draft's Backwards Compatibility +section names as the one way to break a legacy consumer, and ERC-165 offers no way to detect it. + +**Recommendation.** Documentation, not code: state prominently in the README and in the NatSpec of +the no-argument functions that consumers must be pointed at the **subject**, never at the engine, and +that the base functions exist solely for bound subjects calling on their own behalf. + +## 4 — Enumeration cost and removal complexity + +**Severity:** Low · **Effort:** Medium · **Kind:** gas / scalability + +**Where:** `src/DocumentEngineBase.sol:236-245`, `:206-216`, `:150-186` + +- `_removeDocumentName` is a linear scan over the subject's name array. The draft's Reference + Implementation section expects "index tracking to support O(1) removals". `batchRemoveDocuments` + compounds this to O(n·m) and can plausibly exceed the block gas limit for a subject with a large + document set. +- `getAllDocuments` returns the entire array with no paginated alternative. ERC-1643's Security + Considerations explicitly call this out: "Implementations expecting large sets should consider + exposing an additional paginated accessor alongside this interface." + +Neither is a conformance failure. Independently corroborated by Aderyn — L-5 at +`DocumentEngineBase.sol:238`, reached from its "costly operation in a loop" heuristic +([triage](./doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md)). + +**Recommendation.** Add `mapping(address => mapping(bytes32 => uint256)) private _nameIndex` for +O(1) swap-and-pop, plus `getDocumentsPaginated(address subject, uint256 offset, uint256 limit)` and +`getDocumentCount(address subject)`. + +## 5 — The ERC-2771 forwarder is a universal write authority + +**Severity:** Info · **Effort:** Trivial · **Kind:** documentation + +**Where:** `src/DocumentEngine.sol:99-101`; `src/modules/TokenBindingModule.sol:84-88` + +`_msgSender()` drives both `_checkTokenBound()` and the role check. The trusted forwarder can +therefore present itself as any bound subject — writing into that subject's namespace — and as any +role holder. This is the ordinary ERC-2771 trust assumption, but it deserves stating explicitly given +the draft's framing: + +> Subjects should treat the choice of management contract as a permissioning decision, not merely a +> storage one. + +A subject binding to this engine is also trusting the engine's forwarder. `forwarderIrrevocable` is +immutable, which is the right call for predictability, but it also means a compromised forwarder +cannot be revoked — the only remedy is unbinding every subject and migrating. + +**Recommendation.** Document the forwarder as part of every bound subject's trust boundary, in the +README section on ERC-2771 and in the constructor NatSpec. + +## 6 — `_removeDocument` emits before the state change + +**Severity:** Info · **Effort:** Trivial · **Kind:** cosmetic + +**Where:** `src/DocumentEngineBase.sol:258` (emit) before `:260-261` (delete) + +The specs mandate "after state changes" for `setDocument` only, and the implementation complies there +(`:286`). For removal the metadata must be read before deletion, so the current ordering is +convenient; there are no external calls anywhere in the write path, so there is no reentrancy +exposure. + +**Recommendation.** Cosmetic only — the metadata is already cached in the `doc` local, so emitting +after the delete would align both paths at no cost. + +## 7 — Upstream: `IMultiDocumentSubject` manager discovery + +**Severity:** Info · **Kind:** upstream (CMTAT), not this repository + +Not implemented here, and correctly so: the draft is explicit that this interface is "**implemented +by the subject, not by the management contract**", and it is optional for both. No action is required +of the engine. + +There is, however, a near-miss upstream worth aligning. CMTAT's `DocumentEngineModule` already +exposes the same concept under a different shape: + +| draft `IMultiDocumentSubject` | CMTAT `DocumentEngineModule` | +| --- | --- | +| `documentManager() returns (address)` | `documentEngine() returns (IERC1643)` | +| `DocumentManagerUpdated(address previous, address new)` | `DocumentEngine(IERC1643 engine)` — no previous address | +| MUST return `address(0)` when not delegated | ✅ default zero | +| MUST NOT revert | ✅ | + +**Recommendation.** Adding the previous address to the CMTAT event, or an alias getter, would make +CMTAT tokens discoverable by any consumer implementing the draft. That is a change for CMTAT, not for +this repository. + +--- + +## Not open items + +Recorded so they are not re-raised. Verified conformant, several by explicit test: + +- **Subject isolation** — `mapping(address => mapping(bytes32 => Document))` with a per-subject name + array, covered by a fuzz test. A bound subject writes to `_msgSender()` and structurally cannot + reach another namespace. +- **Emission responsibility** — only `DocumentUpdatedForSubject` / `DocumentRemovedForSubject`, never + the base events, as the draft requires of a multi-subject manager. The suite asserts the *absence* + of the base events. +- **`lastModified == 0` as the sole absent-entry sentinel** — respected as both the read convention + and the internal existence check, keeping `uri` / `documentHash` free to be legitimately empty. +- **Null-subject guard on the write path only** — matching the draft's note that guarding + `setDocument` suffices, since removal then fails with `ERC1643MissingDocument()` anyway. +- **`IERC1643MultiDocument` neither inherits nor imports `IERC1643`** — the independence the draft + requires is a property of the file, not a convention. +- **`getDocument` returns the flat ERC-1643 ABI** — pinned by a test that inspects returndata, since + the interface id is identical for both shapes and ERC-165 cannot catch a regression. Both interface + ids are asserted as literals (`0xecfecec8`, `0xa2b1179b`). +- **Unstable ordering after removal** — explicitly permitted by ERC-1643; swap-and-pop is fine. diff --git a/doc/audits/AUDIT_OVERVIEW.md b/doc/audits/AUDIT_OVERVIEW.md index 5955311..a4ed760 100644 --- a/doc/audits/AUDIT_OVERVIEW.md +++ b/doc/audits/AUDIT_OVERVIEW.md @@ -26,7 +26,7 @@ Out of scope: `lib/` (CMTAT, RuleEngine, OpenZeppelin — audited, or not, upstr | --- | --- | --- | --- | | Aderyn `0.6.5` | `v0.4.0` | [report](./tools/v0.4.0/aderyn/aderyn-report.md) | [feedback](./tools/v0.4.0/aderyn/aderyn-report-feedback.md) | | Slither | — | not run | — | -| ERC conformance analysis (AI-assisted) | `v0.4.0` | [`ERC_RESULT.md`](../../ERC_RESULT.md) | — | +| ERC conformance analysis (AI-assisted) | `v0.4.0` | open items: [`IMPROVEMENT.md`](../../IMPROVEMENT.md) | — | ## Static-analysis results @@ -39,7 +39,7 @@ Aderyn reports no Medium or Info categories; it classifies only High and Low. ## Substantive findings fixed in `v0.4.0` -From the ERC conformance analysis ([`ERC_RESULT.md`](../../ERC_RESULT.md)) rather than from the +From the ERC conformance analysis (open items: [`IMPROVEMENT.md`](../../IMPROVEMENT.md)) rather than from the static analyzers — neither tool can see these, since both are ABI- and specification-level: | Finding | Severity | Status | @@ -52,14 +52,15 @@ static analyzers — neither tool can see these, since both are ABI- and specifi ## Known open items Not defects in the sense of being exploitable, but tracked deviations from the specifications. Full -detail in [`ERC_RESULT.md`](../../ERC_RESULT.md) §7. +detail, with a recommendation for each, in [`IMPROVEMENT.md`](../../IMPROVEMENT.md). | Item | Severity | Where | | --- | --- | --- | -| Authorization is not per-`subject`, and `_authorizeDocumentManagement()` takes no `subject`, so a deployment cannot make it per-subject by overriding the hook | High | §4.2 | -| Admin write path has no execution point in the subject, so an ERC-1643 subject emits nothing for writes sent straight to the engine | Medium | §4.3 | -| `_removeDocumentName` is O(n); no paginated enumeration | Low | §4.7 — also surfaced by Aderyn L-5 | -| The ERC-2771 trusted forwarder can act as any bound subject and is immutable | Info | §4.9 | +| Authorization is not per-`subject`, and `_authorizeDocumentManagement()` takes no `subject`, so a deployment cannot make it per-subject by overriding the hook | High | item 1 | +| Admin write path has no execution point in the subject, so an ERC-1643 subject emits nothing for writes sent straight to the engine | Medium | item 2 | +| Engine advertises `IERC1643` but its base functions are `_msgSender()`-scoped, so it is not a usable endpoint for an external consumer | Low | item 3 | +| `_removeDocumentName` is O(n); no paginated enumeration | Low | item 4 — also surfaced by Aderyn L-5 | +| The ERC-2771 trusted forwarder can act as any bound subject and is immutable | Info | item 5 | ## Reporting a vulnerability diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md index bb14b07..7f6a342 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md @@ -18,7 +18,7 @@ operator, a caret pragma, PUSH0, revert-in-loop, storage-writes-in-loop) and one The one result worth keeping in view is **L-5 at `DocumentEngineBase.sol:238`**, which is not a batch loop but the linear scan in `_removeDocumentName`. Aderyn reached it from the "costly operation in a -loop" heuristic; it happens to land on the same code as `ERC_RESULT.md` §4.7, which flags the O(n) +loop" heuristic; it happens to land on the same code as `IMPROVEMENT.md` item 4, which flags the O(n) removal against the multi-subject draft's expectation of "index tracking to support O(1) removals". That is a scalability item, not a vulnerability — a subject with a large document set makes `removeDocument` progressively more expensive, and `batchRemoveDocuments` compounds it to O(n·m). @@ -30,16 +30,16 @@ CMTAT upgrade. | ID | Detector | Sev | Instances | Disposition | Reason (verified against the cited lines) | | --- | --- | --- | --- | --- | --- | -| L-1 | Centralization Risk | Low | 2 | **By design** | `DocumentEngine.sol:24`, `DocumentEngineOwnable.sol:24`. The whole premise of the contract is that a trusted operator manages documents for a fleet of subjects; `DOCUMENT_MANAGER_ROLE` (and `owner`) are that operator. Documented in the README and analysed in `ERC_RESULT.md` §4.2, which argues the privilege should be *narrowed to per-subject*, not removed. Aderyn cannot express that distinction. | +| L-1 | Centralization Risk | Low | 2 | **By design** | `DocumentEngine.sol:24`, `DocumentEngineOwnable.sol:24`. The whole premise of the contract is that a trusted operator manages documents for a fleet of subjects; `DOCUMENT_MANAGER_ROLE` (and `owner`) are that operator. Documented in the README and analysed in `IMPROVEMENT.md` item 1, which argues the privilege should be *narrowed to per-subject*, not removed. Aderyn cannot express that distinction. | | L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** | Every file uses `pragma solidity ^0.8.20;`. The caret is intentional so the sources stay consumable as a library by projects on a different `0.8.x`; the compiler actually used for the deployed bytecode is pinned to `0.8.34` in `foundry.toml`, and `foundry.lock` pins every dependency. Verified: no file uses a construct that behaves differently across the allowed range. | | L-3 | PUSH0 Opcode | Low | 9 | **Environment** | Consequence of `^0.8.20` plus `evm_version = prague`: the compiler emits `PUSH0`, which is unavailable on chains that have not adopted Shanghai. Not a source defect. A deployer targeting such a chain must lower `evm_version` in `foundry.toml` — but CMTAT v3 itself requires `prague`, so that configuration is out of scope for this engine. | | L-4 | Loop Contains `require`/`revert` | Low | 4 | **By design** | `DocumentEngineBase.sol:124, 142, 156, 170` — the four batch loops. The reverts are raised inside `_setDocument` / `_removeDocument` (`ERC1643InvalidName`, `MultiDocumentInvalidSubject`, `ERC1643MissingDocument`). Batch operations are deliberately **all-or-nothing**: a batch containing one bad entry must not half-apply, since partial application would leave the operator unable to tell which documents were written without re-reading every entry. Skipping bad entries instead would silently drop them. | -| L-5 | Costly operations inside loop | Low | 5 | **By design** ×4, **known item** ×1 | Four instances (`:124, 142, 156, 170`) are storage writes in the batch loops — unavoidable, and the reason the batch functions exist is to amortise the 21 000-gas transaction overhead across those writes. The fifth (`:238`) is `_removeDocumentName`'s linear scan with swap-and-pop; see the triage note above and `ERC_RESULT.md` §4.7. | +| L-5 | Costly operations inside loop | Low | 5 | **By design** ×4, **known item** ×1 | Four instances (`:124, 142, 156, 170`) are storage writes in the batch loops — unavoidable, and the reason the batch functions exist is to amortise the 21 000-gas transaction overhead across those writes. The fifth (`:238`) is `_removeDocumentName`'s linear scan with swap-and-pop; see the triage note above and `IMPROVEMENT.md` item 4. | | L-6 | Unchecked Return | Low | 1 | **False positive** | `DocumentEngine.sol:35`, `_grantRole(DEFAULT_ADMIN_ROLE, admin);`. OpenZeppelin's `_grantRole` returns `false` only when the account already holds the role. This call is in the constructor of a freshly deployed contract, where no role has been granted yet, so it always returns `true`; `admin == address(0)` is already rejected on the preceding lines. There is no state to check and no recovery path to take. | ## Delta -This report was regenerated after the `ERC_RESULT.md` §7 items 3 and 6 fixes (error renaming and +This report was regenerated after the error-naming and token-binding fixes (error renaming and relocation; `bindToken`/`unbindToken` null-address rejection and idempotence). **Nothing moved**: the same six detectors fire with the same instance counts, and every cited line is unchanged. nSLOC rose 298 → 307 for the added guard and its NatSpec. diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md index 13917cd..0044169 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md @@ -6,7 +6,7 @@ > | Tool version | `aderyn 0.6.5` | > | Scope | `src/` only — 9 files, 307 nSLOC. **Mocks/tests excluded** (this project has no `src/mocks`; its mocks live in `test/`, which Aderyn does not scan). | > | Result | **0 High · 6 Low · 0 Info** | -> | Verdict | **Nothing to fix.** No finding is exploitable. One (L-5 at `DocumentEngineBase.sol:238`) independently corroborates a known gas/scalability item already tracked as [`ERC_RESULT.md`](../../../../ERC_RESULT.md) §4.7. | +> | Verdict | **Nothing to fix.** No finding is exploitable. One (L-5 at `DocumentEngineBase.sol:238`) independently corroborates a known gas/scalability item already tracked as [`IMPROVEMENT.md`](../../../../IMPROVEMENT.md) item 4. | > > | ID | Detector | Sev | Instances | Assessment | > | --- | --- | --- | --- | --- | diff --git a/src/DocumentEngineBase.sol b/src/DocumentEngineBase.sol index 19ec715..018b563 100644 --- a/src/DocumentEngineBase.sol +++ b/src/DocumentEngineBase.sol @@ -265,7 +265,7 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen // Multi-token guard: `subject` must be a real contract address, never the // null namespace. (The bound-token path passes `_msgSender()`, never zero.) if (subject == address(0)) { - revert ERC1643InvalidSubject(); + revert MultiDocumentInvalidSubject(); } // ERC-1643: reject the null name (ambiguous / default key) if (name_ == bytes32(0)) { diff --git a/src/DocumentEngineInvariant.sol b/src/DocumentEngineInvariant.sol index 86f4469..81e718b 100644 --- a/src/DocumentEngineInvariant.sol +++ b/src/DocumentEngineInvariant.sol @@ -14,14 +14,12 @@ abstract contract DocumentEngineInvariant { error InvalidInputLength(); error AdminWithAddressZeroNotAllowed(); - /// @notice Reverts when a document is set for the null `subject` (`address(0)`). - /// @dev Multi-token extension guard. The multi-subject draft names this condition - /// `MultiDocumentInvalidSubject()`; see - /// `doc/ERCSpecification/erc-draft_multi_document_management.md` and `ERC_RESULT.md` §4.4. - error ERC1643InvalidSubject(); - - // `ERC1643InvalidName()` and `ERC1643MissingDocument()` are NOT declared here: since - // CMTAT v3.3.0-rc2 they are declared by `IERC1643` itself, and the multi-subject draft - // requires a contract implementing both interfaces to obtain each error exactly once - // ("MUST NOT declare them twice"). Re-declaring them is a compile error. + // Only errors that no interface defines belong here. Every specification error is declared by + // the interface that defines its condition, so that an ABI generated from the interface carries + // it and a contract implementing several interfaces obtains each error exactly once — the + // multi-subject draft's "MUST NOT declare them twice", which the compiler also enforces: + // - `ERC1643InvalidName()` / `ERC1643MissingDocument()` → `IERC1643` (since CMTAT v3.3.0-rc2) + // - `MultiDocumentInvalidSubject()` → `IERC1643MultiDocument` + // - `NotBoundToken(address)` → `ITokenBinding` + // - `TokenBindingInvalidToken()` → `ITokenBinding` } diff --git a/src/interfaces/IERC1643MultiDocument.sol b/src/interfaces/IERC1643MultiDocument.sol index 16a98e5..9be7e28 100644 --- a/src/interfaces/IERC1643MultiDocument.sol +++ b/src/interfaces/IERC1643MultiDocument.sol @@ -13,6 +13,15 @@ pragma solidity ^0.8.20; * on-chain product). See `doc/ERCSpecification/erc-draft_multi_document_management.md`. */ interface IERC1643MultiDocument { + /// @notice Reverts when `setDocument` or `removeDocument` is called with `subject == address(0)`. + /// @dev Specific to this proposal; it has no ERC-1643 counterpart, because ERC-1643's + /// `setDocument` has no `subject` argument — its subject is implicitly the contract itself, + /// which is never the null address. Named after the proposal that defines the condition, not + /// after one in which the condition cannot occur; the two errors this interface shares with + /// ERC-1643 (`ERC1643InvalidName`, `ERC1643MissingDocument`) keep their prefix for the opposite + /// reason, and are declared by `IERC1643`, never here. + error MultiDocumentInvalidSubject(); + /// @notice Returns metadata for the document `name` belonging to `subject`. /// @dev Returns the three fields as flat values, matching the specification ABI. A missing /// document yields empty values (`""`, `bytes32(0)`, `0`) and does not revert. diff --git a/src/interfaces/ITokenBinding.sol b/src/interfaces/ITokenBinding.sol index e9ec45c..67cb940 100644 --- a/src/interfaces/ITokenBinding.sol +++ b/src/interfaces/ITokenBinding.sol @@ -13,12 +13,21 @@ pragma solidity ^0.8.20; */ interface ITokenBinding { /// @notice Emitted when a token is bound (`bound = true`) or unbound (`bound = false`). + /// @dev Emitted only when the binding actually changes, so the event stream contains no + /// no-op entries and an indexer can replay it as a sequence of transitions. event TokenBindingSet(address indexed token, bool bound); + /// @notice Thrown when a binding operation targets the null address. + error TokenBindingInvalidToken(); + /// @notice Binds `token`, allowing it to manage its own documents. + /// @dev Idempotent: binding an already-bound token succeeds and emits nothing. + /// Reverts {TokenBindingInvalidToken} when `token` is the null address. function bindToken(address token) external; /// @notice Unbinds `token`. + /// @dev Idempotent: unbinding a token that is not bound succeeds and emits nothing. + /// Reverts {TokenBindingInvalidToken} when `token` is the null address. function unbindToken(address token) external; /// @notice Returns whether `token` is currently bound. diff --git a/src/modules/TokenBindingModule.sol b/src/modules/TokenBindingModule.sol index 245fddc..a65596b 100644 --- a/src/modules/TokenBindingModule.sol +++ b/src/modules/TokenBindingModule.sol @@ -32,8 +32,7 @@ abstract contract TokenBindingModule is DocumentEngineBase, ITokenBinding { */ function bindToken(address token) external virtual override { _authorizeDocumentManagement(); - _boundTokens[token] = true; - emit TokenBindingSet(token, true); + _setTokenBinding(token, true); } /** @@ -42,8 +41,30 @@ abstract contract TokenBindingModule is DocumentEngineBase, ITokenBinding { */ function unbindToken(address token) external virtual override { _authorizeDocumentManagement(); - _boundTokens[token] = false; - emit TokenBindingSet(token, false); + _setTokenBinding(token, false); + } + + /** + * @dev Shared bind/unbind implementation. + * + * Rejects the null address: `address(0)` can never call the engine, so binding it grants + * nothing, but it would still emit a {TokenBindingSet} that off-chain indexers key on — the + * same data-integrity argument the multi-subject draft makes for rejecting a null `subject`. + * + * Writing and emitting only on an actual change makes both functions idempotent and keeps the + * event stream free of no-op entries, so an indexer can treat every {TokenBindingSet} as a real + * transition rather than having to de-duplicate. The repeated call still succeeds, since the + * caller's intent — "this token is (not) bound" — already holds. + */ + function _setTokenBinding(address token, bool bound) internal { + if (token == address(0)) { + revert TokenBindingInvalidToken(); + } + if (_boundTokens[token] == bound) { + return; + } + _boundTokens[token] = bound; + emit TokenBindingSet(token, bound); } /// @inheritdoc ITokenBinding From 60a3d87fa6ab42ada4f99d5ce3df61063079cff9 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Tue, 28 Jul 2026 17:07:14 +0200 Subject: [PATCH 24/47] =?UTF-8?q?=20=20docs:=20regrade=20IMPROVEMENT=20ite?= =?UTF-8?q?m=201=20=E2=80=94=20a=20global=20DOCUMENT=5FMANAGER=5FROLE=20sa?= =?UTF-8?q?tisfies=20the=20draft's=20per-subject=20MUST?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 +- IMPROVEMENT.md | 131 +++++++++++------- README.md | 8 +- doc/audits/AUDIT_OVERVIEW.md | 2 +- .../v0.4.0/aderyn/aderyn-report-feedback.md | 2 +- 5 files changed, 95 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c720fe7..8d56bac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -140,8 +140,8 @@ Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/er Responsibility* rules. The **admin path remains non-conformant by construction** — a write sent straight to the engine has no execution point in the subject, so the subject emits nothing. See [`IMPROVEMENT.md`](./IMPROVEMENT.md) item 2. -- Open conformance gaps are tracked in [`IMPROVEMENT.md`](./IMPROVEMENT.md): per-`subject` - authorization (item 1), admin-path call topology (item 2), and enumeration cost (item 4). +- Open items are tracked in [`IMPROVEMENT.md`](./IMPROVEMENT.md): the most severe is admin-path call + topology (item 2); also authorization granularity (item 1) and enumeration cost (item 4). - CMTAT v3 no longer ships a *standalone* token that consumes an external document engine through its constructor; the standard token stores documents on-chain (`DocumentERC1643Module`). External-engine integration now goes through CMTAT's `DocumentEngineModule` (`setDocumentEngine`). The test suite was updated to exercise this real integration path via a minimal token built on `DocumentEngineModule`. ## v0.3.0 diff --git a/IMPROVEMENT.md b/IMPROVEMENT.md index cf44cda..3eca0a4 100644 --- a/IMPROVEMENT.md +++ b/IMPROVEMENT.md @@ -23,7 +23,7 @@ tracked separately under [`doc/audits/tools/`](./doc/audits/tools). | # | Item | Severity | Effort | Kind | | --- | --- | --- | --- | --- | -| [1](#1--authorization-is-not-per-subject-and-the-hook-cannot-express-it) | Authorization is not per-`subject`, and the hook cannot express it | **High** | Medium | Spec `MUST` | +| [1](#1--authorization-granularity-is-fixed-at-compile-time-the-hook-cannot-express-per-subject-rules) | Authorization granularity is fixed at compile time; the hook cannot express per-`subject` rules | Low¹ | Medium | Extensibility | | [2](#2--the-admin-path-bypasses-subject-side-erc-1643-emission) | Admin path bypasses subject-side ERC-1643 emission | **Medium** | Small–Medium | Spec `SHOULD` | | [3](#3--the-engine-advertises-ierc1643-but-is-not-a-usable-erc-1643-endpoint) | Engine advertises `IERC1643` but is not a usable ERC-1643 endpoint | Low | Trivial | Docs | | [4](#4--enumeration-cost-and-removal-complexity) | Enumeration cost and removal complexity | Low | Medium | Gas | @@ -31,72 +31,109 @@ tracked separately under [`doc/audits/tools/`](./doc/audits/tools). | [6](#6--_removedocument-emits-before-the-state-change) | `_removeDocument` emits before the state change | Info | Trivial | Cosmetic | | [7](#7--upstream-imultidocumentsubject-manager-discovery) | Upstream: `IMultiDocumentSubject` manager discovery | Info | — | Upstream | -Item 1 is the only one that changes what the contract can express; everything else is documentation, -gas, or cosmetic. Item 1 is also **source-compatible** for existing deployments — the default hook -bodies would ignore the new argument — so it does not have to wait for a breaking release. +¹ Low for the single-issuer fleet this engine targets, which is the model the draft sets out to +support. **Medium** only for a deployment shared by unrelated issuers — see item 1 for why that +configuration is not supportable today. + +**Item 2 is the most severe open item.** Item 1 is the only one that changes what the contract can +*express*, and it is source-compatible for existing deployments (the default hook bodies would ignore +the new argument), so it need not wait for a breaking release. Everything else is documentation, gas, +or cosmetic. --- -## 1 — Authorization is not per-`subject`, and the hook cannot express it +## 1 — Authorization granularity is fixed at compile time; the hook cannot express per-`subject` rules -**Severity:** High · **Effort:** Medium · **Kind:** deviation from a specification `MUST` +**Severity:** Low — **Medium** for a deployment shared across unrelated issuers · **Effort:** Medium +· **Kind:** extensibility + deployment guidance -**Where:** `src/DocumentEngineBase.sol:56`, `:71-84`, `:111-186`; `src/DocumentEngine.sol:46-48`; +**Where:** `src/DocumentEngineBase.sol:56`, `:111-186`; `src/DocumentEngine.sol:46-48`; `src/DocumentEngineOwnable.sol:39-41` -> Implementations **MUST** authorize writes per `subject`, so that a caller cannot create, update, or -> remove documents for a `subject` it is not permitted to manage. -> — draft §Authorization +### This is not a conformance failure -`DOCUMENT_MANAGER_ROLE` is a single global permission: every holder may write for *every* subject. -`DocumentEngineOwnable` is the same with `owner`. This is precisely the risk the draft's Security -Considerations open with — "a management contract holds the document sets of unrelated subjects -behind a single address" — and the engine's design is, by default, the unmitigated case. +The draft's requirement is: -That alone might be defensible as a deployment choice, since the project's stated model is a single -operator managing a fleet it owns. The structural problem is that the escape hatch does not work: +> Implementations MUST authorize writes per `subject`, so that a caller cannot create, update, or +> remove documents for a `subject` **it is not permitted to manage**. +> — draft §Authorization -```solidity -function _authorizeDocumentManagement() internal view virtual; // no subject parameter -``` +The operative words are "not permitted to manage", and what a caller is permitted to manage is +defined by the deployment's own access control. `DOCUMENT_MANAGER_ROLE` is a specific, granted role +whose permission covers every subject the engine serves — so there is no subject its holder is *not* +permitted to manage, and the clause is satisfied. Same for `owner` in `DocumentEngineOwnable`. + +This is the case the draft explicitly sets out to support: -A deployment cannot override this hook to implement per-subject rules, because at the point it runs -the subject is not available. In the batch functions the situation is worse — the modifier fires -**once** for the whole call, before any element is read, so even a subject-aware hook would be -bypassed for `batchSetDocuments` / `batchRemoveDocuments`. +> An issuer operating many tokens, funds, or vaults typically maintains one document library and +> **one set of operators**, and duplicating that storage and access-control logic into every subject +> contract is redundant and expensive. +> — draft §Motivation -Consequently the draft's own test case — +A single global operator role over a fleet one issuer controls is that design, not a departure from +it. The draft's test case — "A caller not authorized for a subject failing to create, update, or +remove that subject's documents" — is covered by `testCannotNonAdminSetDocument` and its siblings: an +account without the role is authorized for no subject, and its write reverts. -> A caller not authorized for a subject failing to create, update, or remove that subject's documents. +### What is actually open -— is untestable against this codebase, and is indeed absent from the suite. The existing -`testNonAdmin*` tests cover the globally-unauthorized case only. +The draft's Security Consideration is about **unrelated** subjects: -**Mitigating factor.** The **bound-token path is already per-subject** and cannot be escaped: the -namespace is `_msgSender()`, structurally. A deployment that overrides -`_authorizeDocumentManagement()` to always revert is fully conformant today — but that removes the -admin path entirely rather than scoping it. +> If writes are not authorized per `subject`, any caller permitted to write for one subject can +> modify another subject's legal or operational references. -**Recommendation.** Change the hook signature and call it per subject: +That bites only when one engine instance is shared by parties that do not trust each other — two +issuers, or a service operator hosting documents for external clients. In that deployment a global +role does breach the property, and **this engine cannot currently express the alternative**, because +the authorization hook receives no subject: ```solidity -function _authorizeDocumentManagement(address subject) internal view virtual; - -function batchSetDocuments(address[] calldata subjects, ...) external { - for (uint256 i = 0; i < length; ++i) { - _authorizeDocumentManagement(subjects[i]); - _setDocument(subjects[i], names[i], uris[i], hashes[i]); - } -} +function _authorizeDocumentManagement() internal view virtual; // no subject parameter ``` -The default implementations stay exactly as they are (`_checkRole(DOCUMENT_MANAGER_ROLE)` / -`_checkOwner()`, ignoring `subject`), so behaviour and gas are effectively unchanged, but a -deployment gains the ability to be conformant — for example a per-subject role -`keccak256("DOCUMENT_MANAGER", subject)`. Token binding needs a separate hook -(`_authorizeTokenBinding()`), since binding has no subject. - -Add the draft's missing test case once the hook can express it. +So a deployer cannot subclass their way to per-subject rules; they would have to edit +`DocumentEngineBase`. Two consequences: + +1. **A multi-tenant deployment is not supportable today.** The only conformant option is one engine + instance per trust domain — which is fine, and cheap, but is a deployment constraint that should + be written down rather than discovered. +2. **It contradicts the project's own advertised extension model.** The README and `CLAUDE.md` + promise that a deployment changes *who* is authorized by overriding a hook, "not by editing the + management functions". That holds for swapping roles for an owner; it does not hold for making the + decision depend on the subject. The hook is the documented seam, and this is the one axis it + cannot turn. + +Related detail, relevant only if the hook ever gains a subject: in the batch functions the modifier +fires **once** for the whole call, before any element is read, so a subject-aware hook would have to +be invoked inside the loops rather than via the modifier. + +### Recommendation + +Low priority, and **not** required for the single-issuer model this engine targets. Two options: + +- *Documentation only* (sufficient today): state in the README that one engine instance serves one + trust domain, and that unrelated issuers should each deploy their own rather than share one. +- *Enable the axis*, if multi-tenant support is ever wanted: + + ```solidity + function _authorizeDocumentManagement(address subject) internal view virtual; + + function batchSetDocuments(address[] calldata subjects, ...) external { + for (uint256 i = 0; i < length; ++i) { + _authorizeDocumentManagement(subjects[i]); + _setDocument(subjects[i], names[i], uris[i], hashes[i]); + } + } + ``` + + The default implementations stay exactly as they are (`_checkRole(DOCUMENT_MANAGER_ROLE)` / + `_checkOwner()`, ignoring `subject`), so behaviour and gas are unchanged and no existing deployment + is affected — a subclass simply gains the option of a per-subject rule such as + `keccak256("DOCUMENT_MANAGER", subject)`. Token binding would need a separate hook + (`_authorizeTokenBinding()`), since binding has no subject. + +Note also that the bound-token path is already per-subject and cannot be escaped: the namespace is +`_msgSender()`, structurally. A subject that manages its own documents is unaffected by any of this. ## 2 — The admin path bypasses subject-side ERC-1643 emission diff --git a/README.md b/README.md index 968de1b..7e71694 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ > This project has not been audited yet, please use at your own risk. For any questions, please contact [admin@cmta.ch](mailto:admin@cmta.ch). > +> **Known open items** are tracked in **[`IMPROVEMENT.md`](./IMPROVEMENT.md)**. None is an +> exploitable vulnerability, but integrators should read it before relying on the engine — in +> particular item 2 (a write sent straight to the engine leaves an ERC-1643 subject's own events +> unemitted) and item 1 (one engine instance serves **one trust domain**: `DOCUMENT_MANAGER_ROLE` +> covers every subject, so unrelated issuers should each deploy their own engine rather than share +> one). The `DocumentEngine` is an external contract to manage documents through [*ERC-1643*](https://github.com/ethereum/EIPs/issues/1643), a standard proposition to manage document on-chain. This standard is notably used by [ERC-1400](https://github.com/ethereum/eips/issues/1411) from Polymath. @@ -364,7 +370,7 @@ slither . --checklist --filter-paths "node_modules,test,forge-std,CMTAT,openzepp > **Static-analysis output is leads, not findings.** Every dismissal in the feedback files was > verified against the cited `file:line`, and neither tool can see the specification-level issues -> that matter most here — those are in [`ERC_RESULT.md`](./ERC_RESULT.md). +> that matter most here — those are in [`IMPROVEMENT.md`](./IMPROVEMENT.md). ### Surya diff --git a/doc/audits/AUDIT_OVERVIEW.md b/doc/audits/AUDIT_OVERVIEW.md index a4ed760..4676427 100644 --- a/doc/audits/AUDIT_OVERVIEW.md +++ b/doc/audits/AUDIT_OVERVIEW.md @@ -56,7 +56,7 @@ detail, with a recommendation for each, in [`IMPROVEMENT.md`](../../IMPROVEMENT. | Item | Severity | Where | | --- | --- | --- | -| Authorization is not per-`subject`, and `_authorizeDocumentManagement()` takes no `subject`, so a deployment cannot make it per-subject by overriding the hook | High | item 1 | +| `_authorizeDocumentManagement()` takes no `subject`, so a deployment cannot make authorization per-subject by overriding the hook. Conformant for the single-issuer fleet the engine targets — `DOCUMENT_MANAGER_ROLE` is permitted to manage every subject — but it means one instance serves one trust domain | Low (Medium if shared across unrelated issuers) | item 1 | | Admin write path has no execution point in the subject, so an ERC-1643 subject emits nothing for writes sent straight to the engine | Medium | item 2 | | Engine advertises `IERC1643` but its base functions are `_msgSender()`-scoped, so it is not a usable endpoint for an external consumer | Low | item 3 | | `_removeDocumentName` is O(n); no paginated enumeration | Low | item 4 — also surfaced by Aderyn L-5 | diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md index 7f6a342..3bfe2fb 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md @@ -30,7 +30,7 @@ CMTAT upgrade. | ID | Detector | Sev | Instances | Disposition | Reason (verified against the cited lines) | | --- | --- | --- | --- | --- | --- | -| L-1 | Centralization Risk | Low | 2 | **By design** | `DocumentEngine.sol:24`, `DocumentEngineOwnable.sol:24`. The whole premise of the contract is that a trusted operator manages documents for a fleet of subjects; `DOCUMENT_MANAGER_ROLE` (and `owner`) are that operator. Documented in the README and analysed in `IMPROVEMENT.md` item 1, which argues the privilege should be *narrowed to per-subject*, not removed. Aderyn cannot express that distinction. | +| L-1 | Centralization Risk | Low | 2 | **By design** | `DocumentEngine.sol:24`, `DocumentEngineOwnable.sol:24`. The whole premise of the contract is that a trusted operator manages documents for a fleet of subjects; `DOCUMENT_MANAGER_ROLE` (and `owner`) are that operator. Documented in the README and analysed in `IMPROVEMENT.md` item 1, which concludes the global role is the correct model for the single-issuer fleet this engine targets. Aderyn cannot express that distinction. | | L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** | Every file uses `pragma solidity ^0.8.20;`. The caret is intentional so the sources stay consumable as a library by projects on a different `0.8.x`; the compiler actually used for the deployed bytecode is pinned to `0.8.34` in `foundry.toml`, and `foundry.lock` pins every dependency. Verified: no file uses a construct that behaves differently across the allowed range. | | L-3 | PUSH0 Opcode | Low | 9 | **Environment** | Consequence of `^0.8.20` plus `evm_version = prague`: the compiler emits `PUSH0`, which is unavailable on chains that have not adopted Shanghai. Not a source defect. A deployer targeting such a chain must lower `evm_version` in `foundry.toml` — but CMTAT v3 itself requires `prague`, so that configuration is out of scope for this engine. | | L-4 | Loop Contains `require`/`revert` | Low | 4 | **By design** | `DocumentEngineBase.sol:124, 142, 156, 170` — the four batch loops. The reverts are raised inside `_setDocument` / `_removeDocument` (`ERC1643InvalidName`, `MultiDocumentInvalidSubject`, `ERC1643MissingDocument`). Batch operations are deliberately **all-or-nothing**: a batch containing one bad entry must not half-apply, since partial application would leave the operator unable to tell which documents were written without re-reading every entry. Skipping bad entries instead would silently drop them. | From bbf23bebbd55e1f1a1658eb0d4fda5211f4df3ac Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Tue, 28 Jul 2026 17:17:25 +0200 Subject: [PATCH 25/47] =?UTF-8?q?revert:=20keep=20advertising=20type(IERC1?= =?UTF-8?q?643).interfaceId=20=E2=80=94=20a=20token=20uses=20it=20to=20che?= =?UTF-8?q?ck=20the=20base=20endpoints=20exist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 5 ++++- CHANGELOG.md | 4 +++- CLAUDE.md | 5 ++++- IMPROVEMENT.md | 32 ++++++++++++++------------------ README.md | 27 +++++++++++++++++++++++++++ doc/audits/AUDIT_OVERVIEW.md | 2 +- src/DocumentEngine.sol | 22 +++++++++++++++++----- src/DocumentEngineOwnable.sol | 6 ++++-- test/DocumentEngine.t.sol | 29 +++++++++++++++++++++++++++-- 9 files changed, 101 insertions(+), 31 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 95b69ad..00911de 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,7 +53,10 @@ addressed by a `bytes32` name. `TokenBindingInvalidToken()`. - **ERC-1643 conformance:** `setDocument` reverts `ERC1643InvalidName()` on `name == 0`; `removeDocument` reverts `ERC1643MissingDocument()` on a missing doc; - `supportsInterface` advertises `IERC1643` + `IERC1643MultiDocument` (both deployments). + `supportsInterface` advertises `IERC1643` + `IERC1643MultiDocument` + `ITokenBinding` + (both deployments). The base id is for a **token** checking that the single-argument + endpoints exist before wiring itself to the engine — it does **not** mean documents + should be read from the engine's address, since those functions are `_msgSender()`-scoped. Both errors are declared by `IERC1643` itself since CMTAT `v3.3.0-rc2` — do **not** re-declare them in `DocumentEngineInvariant` (duplicate declaration = compile error, and the multi-subject draft forbids it). diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d56bac..f39cec1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -124,7 +124,9 @@ Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/er - **Emission responsibility.** As a shared, multi-token manager the engine now emits **only** the address-carrying extension events and **no longer** emits the base `DocumentUpdated` / `DocumentRemoved` events (the spec's `MUST NOT` for a shared manager — those events carry no `subject` and belong on the token contract). - **Extension events/interface.** Renamed the multi-token events to the standard `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` (parameter `subject`), and introduced the `IERC1643MultiDocument` interface (`src/interfaces/IERC1643MultiDocument.sol`) that the base now implements — the address-scoped `getDocument` / `getAllDocuments` / `setDocument` / `removeDocument`. - **Input validation.** `setDocument` now reverts `ERC1643InvalidName()` when `name == bytes32(0)` and `MultiDocumentInvalidSubject()` when `subject == address(0)` (the multi-subject draft's null-namespace guard); `removeDocument` now reverts `ERC1643MissingDocument()` for a non-existent document (previously it silently emitted a spurious removal event). See [`erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md) for the corresponding multi-subject draft. -- **ERC-165 discovery.** `supportsInterface` now returns `true` for `type(IERC1643).interfaceId` and `type(IERC1643MultiDocument).interfaceId` (both deployments). +- **ERC-165 discovery.** `supportsInterface` now returns `true` for `type(IERC1643).interfaceId`, `type(IERC1643MultiDocument).interfaceId` and `type(ITokenBinding).interfaceId` (both deployments). + + The base id is advertised because the engine implements the base single-argument functions, and because a **token** uses it: before wiring itself to the engine with `setDocumentEngine(engine)`, or before forwarding `setDocument(name, uri, hash)`, it can confirm through ERC-165 that those endpoints exist. It does **not** mean documents should be read from the engine's address — the base functions are `_msgSender()`-scoped, so a third-party read returns the caller's own empty namespace. Documented in the README and asserted by `testBaseERC1643IsAdvertisedButReadsAreCallerScoped`. ### Added (token binding) diff --git a/CLAUDE.md b/CLAUDE.md index 95b69ad..00911de 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -53,7 +53,10 @@ addressed by a `bytes32` name. `TokenBindingInvalidToken()`. - **ERC-1643 conformance:** `setDocument` reverts `ERC1643InvalidName()` on `name == 0`; `removeDocument` reverts `ERC1643MissingDocument()` on a missing doc; - `supportsInterface` advertises `IERC1643` + `IERC1643MultiDocument` (both deployments). + `supportsInterface` advertises `IERC1643` + `IERC1643MultiDocument` + `ITokenBinding` + (both deployments). The base id is for a **token** checking that the single-argument + endpoints exist before wiring itself to the engine — it does **not** mean documents + should be read from the engine's address, since those functions are `_msgSender()`-scoped. Both errors are declared by `IERC1643` itself since CMTAT `v3.3.0-rc2` — do **not** re-declare them in `DocumentEngineInvariant` (duplicate declaration = compile error, and the multi-subject draft forbids it). diff --git a/IMPROVEMENT.md b/IMPROVEMENT.md index 3eca0a4..ccbcbb4 100644 --- a/IMPROVEMENT.md +++ b/IMPROVEMENT.md @@ -25,12 +25,15 @@ tracked separately under [`doc/audits/tools/`](./doc/audits/tools). | --- | --- | --- | --- | --- | | [1](#1--authorization-granularity-is-fixed-at-compile-time-the-hook-cannot-express-per-subject-rules) | Authorization granularity is fixed at compile time; the hook cannot express per-`subject` rules | Low¹ | Medium | Extensibility | | [2](#2--the-admin-path-bypasses-subject-side-erc-1643-emission) | Admin path bypasses subject-side ERC-1643 emission | **Medium** | Small–Medium | Spec `SHOULD` | -| [3](#3--the-engine-advertises-ierc1643-but-is-not-a-usable-erc-1643-endpoint) | Engine advertises `IERC1643` but is not a usable ERC-1643 endpoint | Low | Trivial | Docs | | [4](#4--enumeration-cost-and-removal-complexity) | Enumeration cost and removal complexity | Low | Medium | Gas | | [5](#5--the-erc-2771-forwarder-is-a-universal-write-authority) | ERC-2771 forwarder is a universal write authority | Info | Trivial | Docs | | [6](#6--_removedocument-emits-before-the-state-change) | `_removeDocument` emits before the state change | Info | Trivial | Cosmetic | | [7](#7--upstream-imultidocumentsubject-manager-discovery) | Upstream: `IMultiDocumentSubject` manager discovery | Info | — | Upstream | +Item numbers are stable identifiers, referenced from `CHANGELOG.md` and the audit reports; a resolved +item's number is retired rather than reused. **Item 3 was closed in `v0.4.0`** by documenting what +`type(IERC1643).interfaceId` does and does not promise on this address. + ¹ Low for the single-issuer fleet this engine targets, which is the model the draft sets out to support. **Medium** only for a deployment shared by unrelated issuers — see item 1 for why that configuration is not supportable today. @@ -186,23 +189,6 @@ contract signals the difference. - *Callback*: add the draft's "manager-initiated with callback" topology — an optional permissioned hook on the subject invoked after the write, so the subject emits. -## 3 — The engine advertises `IERC1643` but is not a usable ERC-1643 endpoint - -**Severity:** Low · **Effort:** Trivial · **Kind:** documentation - -**Where:** `src/DocumentEngine.sol:88`; `src/DocumentEngineOwnable.sol:47-49` - -This is permitted — the draft's rule is that a contract may advertise `type(IERC1643).interfaceId` -*only if* it implements the base functions, and the engine does. But those functions are -`_msgSender()`-scoped (`DocumentEngineBase.sol:181-216`), so a consumer that ERC-165-detects -ERC-1643 on the **engine** address and then calls `getDocument(name)` receives empty values, no -revert, and never sees a base event. This is the deployment error the draft's Backwards Compatibility -section names as the one way to break a legacy consumer, and ERC-165 offers no way to detect it. - -**Recommendation.** Documentation, not code: state prominently in the README and in the NatSpec of -the no-argument functions that consumers must be pointed at the **subject**, never at the engine, and -that the base functions exist solely for bound subjects calling on their own behalf. - ## 4 — Enumeration cost and removal complexity **Severity:** Low · **Effort:** Medium · **Kind:** gas / scalability @@ -304,3 +290,13 @@ Recorded so they are not re-raised. Verified conformant, several by explicit tes the interface id is identical for both shapes and ERC-165 cannot catch a regression. Both interface ids are asserted as literals (`0xecfecec8`, `0xa2b1179b`). - **Unstable ordering after removal** — explicitly permitted by ERC-1643; swap-and-pop is fine. +- **ERC-165 advertises `type(IERC1643).interfaceId`** (was item 3, closed in `v0.4.0` by + documentation, which was the original recommendation). The base single-argument functions exist, + which is what the draft conditions the id on, and a **token** uses the advertisement to confirm + those endpoints before wiring itself to the engine. The caveat it does *not* cover — those + functions are `_msgSender()`-scoped, so a third party reading `getDocument(name)` from the engine + gets its own empty namespace instead of the subject's documents, silently — is now stated in the + README, in the `supportsInterface` NatSpec of both deployments, and asserted by + `testBaseERC1643IsAdvertisedButReadsAreCallerScoped`. Briefly removed during `v0.4.0` development + and restored: dropping the id would have made a token's legitimate capability check fail in order + to discourage a misuse that documentation addresses directly. diff --git a/README.md b/README.md index 7e71694..5d9955a 100644 --- a/README.md +++ b/README.md @@ -232,6 +232,33 @@ interface IERC8303 { - ERC-165 discovery is supported: `supportsInterface(0x54fd4d50)` (the ERC-8303 interface id) returns `true`. +### ERC-165: what the engine advertises + +Both deployments advertise: + +| Interface | Id | | +| --- | --- | --- | +| `IERC1643` | `0xecfecec8` | base single-argument functions, for a **bound subject** | +| `IERC1643MultiDocument` | `0xa2b1179b` | address-scoped document management | +| `ITokenBinding` | — | `bindToken` / `unbindToken` / `isTokenBound` | +| `IERC8303` | `0x54fd4d50` | `version()` | +| `IERC165` | `0x01ffc9a7` | | +| `IAccessControlEnumerable` | — | `DocumentEngine` only | + +`type(IERC1643).interfaceId` is advertised because the engine really does implement the base +single-argument functions. Its audience is a **token wiring itself to the engine**: before calling +`setDocumentEngine(engine)`, or before forwarding `setDocument(name, uri, hash)`, a token can confirm +through ERC-165 that those endpoints exist here rather than discovering it from a failed call. +`ITokenBinding` answers the complementary question — does this engine have a binding surface — and +`isTokenBound(address(this))` whether that particular token may use it. + +> **It is not an invitation to read documents from this address.** The base functions are +> `_msgSender()`-scoped, so a third party calling `getDocument(name)` on the engine reads *its own*, +> empty namespace — no revert, no error, just nothing — and the engine emits only the +> address-carrying `*ForSubject` events. Point document consumers at the **subject**, or use the +> address-scoped `getDocument(subject, name)`. Asserted by +> `testBaseERC1643IsAdvertisedButReadsAreCallerScoped`. + ## Schema ### Inheritance diff --git a/doc/audits/AUDIT_OVERVIEW.md b/doc/audits/AUDIT_OVERVIEW.md index 4676427..94f975b 100644 --- a/doc/audits/AUDIT_OVERVIEW.md +++ b/doc/audits/AUDIT_OVERVIEW.md @@ -48,6 +48,7 @@ static analyzers — neither tool can see these, since both are ABI- and specifi | `ERC1643InvalidName` / `ERC1643MissingDocument` declared both locally and by `IERC1643`, which the multi-subject draft forbids and the compiler rejects | Blocker | **Fixed** — local declarations removed | | Null-subject error named `ERC1643InvalidSubject`, after a standard in which the condition cannot occur, and declared on an abstract contract rather than an interface | Low | **Fixed** — renamed `MultiDocumentInvalidSubject` and moved to `IERC1643MultiDocument`; every specification error now sits on the interface defining its condition | | `bindToken(address(0))` accepted, and bind/unbind emitted `TokenBindingSet` even when the binding did not change | Low | **Fixed** — null address rejected with `TokenBindingInvalidToken()`; both are now idempotent and emit only on a real transition | +| ERC-165 advertises `type(IERC1643).interfaceId`, which a token uses to check the base endpoints exist, but which a third party could misread as "read documents here" — the base functions are caller-scoped, so such a read silently returns an empty namespace | Low | **Documented** — the id is kept for the token's capability check; the caveat is stated in the README and both `supportsInterface` NatSpecs, and asserted by a test | ## Known open items @@ -58,7 +59,6 @@ detail, with a recommendation for each, in [`IMPROVEMENT.md`](../../IMPROVEMENT. | --- | --- | --- | | `_authorizeDocumentManagement()` takes no `subject`, so a deployment cannot make authorization per-subject by overriding the hook. Conformant for the single-issuer fleet the engine targets — `DOCUMENT_MANAGER_ROLE` is permitted to manage every subject — but it means one instance serves one trust domain | Low (Medium if shared across unrelated issuers) | item 1 | | Admin write path has no execution point in the subject, so an ERC-1643 subject emits nothing for writes sent straight to the engine | Medium | item 2 | -| Engine advertises `IERC1643` but its base functions are `_msgSender()`-scoped, so it is not a usable endpoint for an external consumer | Low | item 3 | | `_removeDocumentName` is O(n); no paginated enumeration | Low | item 4 — also surfaced by Aderyn L-5 | | The ERC-2771 trusted forwarder can act as any bound subject and is immutable | Info | item 5 | diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index 7b1a17e..5cfdff8 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -71,11 +71,23 @@ contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnume } /** - * @dev ERC-165 discovery: advertises ERC-1643 and its multi-token extension, - * plus the version module (ERC-8303) and `AccessControlEnumerable`. - * The engine implements the base single-argument functions, so it advertises - * `type(IERC1643).interfaceId`; it also implements the address-scoped - * extension, so it advertises `type(IERC1643MultiDocument).interfaceId`. + * @dev ERC-165 discovery: advertises ERC-1643 and its multi-subject extension, the token-binding + * surface, the version module (ERC-8303) and `AccessControlEnumerable`. + * + * `type(IERC1643).interfaceId` is advertised because the engine does implement the base + * single-argument functions, which is exactly what the draft conditions the id on. Its audience + * is a **token wiring itself to this engine**: before calling `setDocumentEngine(engine)`, or + * before forwarding `setDocument(name, uri, hash)` to it, a token can confirm through ERC-165 + * that the single-argument ERC-1643 endpoints exist here, rather than finding out from a failed + * call. `type(ITokenBinding).interfaceId` answers the complementary question — whether this + * engine has a binding surface at all — and `isTokenBound(address(this))` whether that + * particular token may use it. + * + * It is **not** an invitation to read documents from this address. The base functions are + * `_msgSender()`-scoped, so a consumer calling `getDocument(name)` here reads its own, empty + * namespace, and this engine emits only the address-carrying `*ForSubject` events. Point + * document consumers at the **subject**, or use the address-scoped `getDocument(subject, name)`. + * * See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) diff --git a/src/DocumentEngineOwnable.sol b/src/DocumentEngineOwnable.sol index e8227cc..c6ce4a1 100644 --- a/src/DocumentEngineOwnable.sol +++ b/src/DocumentEngineOwnable.sol @@ -41,8 +41,10 @@ contract DocumentEngineOwnable is TokenBindingModule, VersionModule, Ownable2Ste } /** - * @dev ERC-165 discovery: advertises ERC-1643 and its multi-token extension, - * plus the version module (ERC-8303). See {IERC165-supportsInterface}. + * @dev ERC-165 discovery: advertises ERC-1643 and its multi-subject extension, the token-binding + * surface and the version module (ERC-8303). See the rationale on + * {DocumentEngine-supportsInterface} for what `type(IERC1643).interfaceId` does and does not + * tell a caller here. See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(VersionModule) returns (bool) { return interfaceId == type(IERC1643).interfaceId || interfaceId == type(IERC1643MultiDocument).interfaceId diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index 9ea7d81..fc3e15a 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -7,6 +7,8 @@ import "../src/DocumentEngineInvariant.sol"; import "OZ/access/AccessControl.sol"; import {IERC165} from "OZ/utils/introspection/IERC165.sol"; import {IERC8303} from "../src/interfaces/IERC8303.sol"; +// Imported explicitly rather than relied on transitively through DocumentEngine.sol. +import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; import {IERC1643MultiDocument} from "../src/interfaces/IERC1643MultiDocument.sol"; import {ITokenBinding} from "../src/interfaces/ITokenBinding.sol"; import {TokenBindingModule} from "../src/modules/TokenBindingModule.sol"; @@ -369,14 +371,37 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { assertEq(type(IERC1643).interfaceId, bytes4(0xecfecec8)); assertEq(type(IERC1643MultiDocument).interfaceId, bytes4(0xa2b1179b)); - // implements the base single-argument functions... + // implements the base single-argument functions, so a token can detect them here... assertTrue(documentEngine.supportsInterface(type(IERC1643).interfaceId)); - // ...and the address-scoped multi-token extension + // ...the address-scoped multi-subject interface... assertTrue(documentEngine.supportsInterface(type(IERC1643MultiDocument).interfaceId)); // ...and the shared token-binding surface assertTrue(documentEngine.supportsInterface(type(ITokenBinding).interfaceId)); } + /** + * @dev What `type(IERC1643).interfaceId` does and does not promise here. + * + * It promises the base single-argument functions exist, which is what a token checks before + * wiring itself to the engine. It does **not** make this address a document endpoint for third + * parties: those functions are `_msgSender()`-scoped, so an external reader gets its own empty + * namespace rather than the subject's documents — silently, with no revert. That asymmetry is + * asserted here so it stays a documented property rather than a surprise. + */ + function testBaseERC1643IsAdvertisedButReadsAreCallerScoped() public { + assertTrue(documentEngine.supportsInterface(type(IERC1643).interfaceId)); + + // `documentName` exists — but only under `testContract`, not under an arbitrary reader. + (,, uint256 lastModifiedForSubject) = documentEngine.getDocument(testContract, documentName); + assertGt(lastModifiedForSubject, 0); + + vm.prank(user); + (string memory uri, bytes32 hash_, uint256 lastModified) = documentEngine.getDocument(documentName); + assertEq(uri, ""); + assertEq(hash_, bytes32(0)); + assertEq(lastModified, 0, "a caller-scoped read returns the caller's own namespace, not the subject's"); + } + /** * @dev Pins the `getDocument` wire format to the flat ERC-1643 ABI. * From 4f128800de51ee551f869e1abb90cd0161748330 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 10:35:54 +0200 Subject: [PATCH 26/47] chore: upgrade OpenZeppelin to v5.7.0, record CMTAT v3.3.0-rc3 / RuleEngine v3.0.0-rc5 --- AGENTS.md | 7 +-- CHANGELOG.md | 21 +++++---- CLAUDE.md | 7 +-- IMPROVEMENT.md | 2 +- README.md | 20 +++++---- erc-1643-proposition.md | 59 ++++++++++++++++++++++++++ foundry.lock | 16 +++---- lib/CMTAT | 2 +- lib/RuleEngine | 2 +- lib/openzeppelin-contracts | 2 +- lib/openzeppelin-contracts-upgradeable | 2 +- 11 files changed, 104 insertions(+), 36 deletions(-) create mode 100644 erc-1643-proposition.md diff --git a/AGENTS.md b/AGENTS.md index 00911de..31f53f0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -57,7 +57,8 @@ addressed by a `bytes32` name. (both deployments). The base id is for a **token** checking that the single-argument endpoints exist before wiring itself to the engine — it does **not** mean documents should be read from the engine's address, since those functions are `_msgSender()`-scoped. - Both errors are declared by `IERC1643` itself since CMTAT `v3.3.0-rc2` — do **not** + Both errors are declared by `IERC1643` itself since CMTAT `v3.3.0-rc2` (still true in + `v3.3.0-rc3`) — do **not** re-declare them in `DocumentEngineInvariant` (duplicate declaration = compile error, and the multi-subject draft forbids it). - **`getDocument` returns flat values**, `(string uri, bytes32 documentHash, @@ -156,8 +157,8 @@ Other important files: ## Dependencies (tested versions) -- CMTAT `v3.3.0-rc2`, RuleEngine `v3.0.0-rc4` (binding-pattern reference only; compliance module not reused) -- OpenZeppelin Contracts / Contracts Upgradeable `v5.6.1` +- CMTAT `v3.3.0-rc3`, RuleEngine `v3.0.0-rc5` (binding-pattern reference only; compliance module not reused) +- OpenZeppelin Contracts / Contracts Upgradeable `v5.7.0` - Solidity `0.8.34`, Foundry ## Common commands diff --git a/CHANGELOG.md b/CHANGELOG.md index f39cec1..f591acb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,7 +43,7 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) ## v0.4.0 -Targets **CMTAT `v3.3.0-rc2`** — see the [compatibility matrix](./README.md#version-compatibility) +Targets **CMTAT `v3.3.0-rc3`** — see the [compatibility matrix](./README.md#version-compatibility) for which CMTAT release each version of this engine is built against. > **Versioning note.** `getDocument` changes shape relative to `v0.3.0`, which the convention above @@ -54,20 +54,23 @@ for which CMTAT release each version of this engine is built against. ### Changed - **Dependencies** - - Upgrade CMTAT `v2.5.0-rc0` → [`v3.3.0-rc2`](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc2) - (`lib/CMTAT` → `35d8940b40943828c5ea407dc6b22d559d92e4ae`). Development passed through - `v3.3.0-rc1`; that interim release is **not** compatible with the code as shipped here, because - it declares neither the ERC-1643 errors nor the flat `getDocument` return (see below). - - Upgrade OpenZeppelin Contracts (and Contracts Upgradeable) `v5.0.2` → `v5.6.1` - - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) `v3.0.0-rc4` as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) + - Upgrade CMTAT `v2.5.0-rc0` → [`v3.3.0-rc3`](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) + (`lib/CMTAT` → `658672f190d56d3f61663a7d6d51962b8980df70`). Development passed through + `v3.3.0-rc1` and `v3.3.0-rc2`. rc1 is **not** compatible with the code as shipped here, because + it declares neither the ERC-1643 errors nor the flat `getDocument` return (see below); rc2 and + rc3 are interchangeable for this engine — between them, the whole document surface + (`draft-IERC1643.sol`, `IDocumentEngine.sol`, `DocumentEngineModule.sol`, + `DocumentERC1643Module.sol`) changed only its pragma, `^0.8.20` → `^0.8.24`. + - Upgrade OpenZeppelin Contracts (and Contracts Upgradeable) `v5.0.2` → [`v5.7.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.7.0) + - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) [`v3.0.0-rc5`](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) - `foundry.lock` now records every submodule by tag; all five entries had gone stale since `v0.3.0`. - **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) - **`IERC1643` (CMTAT v3) breaking changes** - `getDocument` keeps returning `(string uri, bytes32 documentHash, uint256 lastModified)` — the flat ERC-1643 ABI — on **both** overloads, `getDocument(bytes32)` and `getDocument(address subject, bytes32)`. CMTAT `v3.3.0-rc1` briefly replaced this with a - `Document` struct and `v3.3.0-rc2` reverted it; this engine follows rc2, so relative to `v0.3.0` - the external shape is unchanged. + `Document` struct and `v3.3.0-rc2` reverted it; this engine follows rc2/rc3, so relative to + `v0.3.0` the external shape is unchanged. The distinction is worth recording because it is invisible to interface detection: return types are not part of a function signature, so both shapes share the same selectors and the same diff --git a/CLAUDE.md b/CLAUDE.md index 00911de..31f53f0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,7 +57,8 @@ addressed by a `bytes32` name. (both deployments). The base id is for a **token** checking that the single-argument endpoints exist before wiring itself to the engine — it does **not** mean documents should be read from the engine's address, since those functions are `_msgSender()`-scoped. - Both errors are declared by `IERC1643` itself since CMTAT `v3.3.0-rc2` — do **not** + Both errors are declared by `IERC1643` itself since CMTAT `v3.3.0-rc2` (still true in + `v3.3.0-rc3`) — do **not** re-declare them in `DocumentEngineInvariant` (duplicate declaration = compile error, and the multi-subject draft forbids it). - **`getDocument` returns flat values**, `(string uri, bytes32 documentHash, @@ -156,8 +157,8 @@ Other important files: ## Dependencies (tested versions) -- CMTAT `v3.3.0-rc2`, RuleEngine `v3.0.0-rc4` (binding-pattern reference only; compliance module not reused) -- OpenZeppelin Contracts / Contracts Upgradeable `v5.6.1` +- CMTAT `v3.3.0-rc3`, RuleEngine `v3.0.0-rc5` (binding-pattern reference only; compliance module not reused) +- OpenZeppelin Contracts / Contracts Upgradeable `v5.7.0` - Solidity `0.8.34`, Foundry ## Common commands diff --git a/IMPROVEMENT.md b/IMPROVEMENT.md index ccbcbb4..79f65e9 100644 --- a/IMPROVEMENT.md +++ b/IMPROVEMENT.md @@ -1,7 +1,7 @@ # IMPROVEMENT — open items Known deviations, gaps and improvement opportunities in `DocumentEngine`, carried forward as of -**`v0.4.0`** (CMTAT `v3.3.0-rc2`). +**`v0.4.0`** (CMTAT `v3.3.0-rc3`). They come from a clause-by-clause conformance analysis of the implementation against the two specifications this engine implements: diff --git a/README.md b/README.md index 5d9955a..525818e 100644 --- a/README.md +++ b/README.md @@ -329,11 +329,11 @@ The toolchain includes the following components, where the versions are the late - Foundry - Solidity 0.8.34 (via solc-js), `evm_version = prague` -- OpenZeppelin Contracts (submodule) [v5.6.1](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.6.1) +- OpenZeppelin Contracts (submodule) [v5.7.0](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.7.0) - Tests - - [CMTAT v3.3.0-rc2](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc2) - - [RuleEngine v3.0.0-rc4](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc4) (binding-pattern reference only — its compliance module is [not reused](#why-not-reuse-ruleengines-erc-3643-compliance-module)) - - OpenZeppelin Contracts Upgradeable (submodule) [v5.6.1](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v5.6.1) + - [CMTAT v3.3.0-rc3](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) + - [RuleEngine v3.0.0-rc5](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) (binding-pattern reference only — its compliance module is [not reused](#why-not-reuse-ruleengines-erc-3643-compliance-module)) + - OpenZeppelin Contracts Upgradeable (submodule) [v5.7.0](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v5.7.0) ### Version compatibility @@ -343,7 +343,7 @@ CMTAT than the one below is not supported. | DocumentEngine | CMTAT | Solidity / `evm_version` | OpenZeppelin | `getDocument` returns | | -------------- | ----- | ------------------------ | ------------ | --------------------- | -| **v0.4.0** (current) | [v3.3.0-rc2](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc2) | `0.8.34` / `prague` | v5.6.1 | `(string, bytes32, uint256)` | +| **v0.4.0** (current) | [v3.3.0-rc3](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) | `0.8.34` / `prague` | v5.7.0 | `(string, bytes32, uint256)` | | v0.3.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | | v0.2.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | | v0.1.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | @@ -351,9 +351,13 @@ CMTAT than the one below is not supported. Notes on the CMTAT v2 → v3 jump at `v0.4.0`: - **CMTAT `v3.3.0-rc1` is not supported.** It is the one release in which `IERC1643.getDocument` - returns a `Document` struct rather than the three flat values; `v3.3.0-rc2` reverted that. rc1 also - does not declare `ERC1643InvalidName` / `ERC1643MissingDocument` on the interface. Building this - engine against rc1 fails to compile. + returns a `Document` struct rather than the three flat values; `v3.3.0-rc2` reverted that and + `v3.3.0-rc3` keeps the flat return. rc1 also does not declare `ERC1643InvalidName` / + `ERC1643MissingDocument` on the interface. Building this engine against rc1 fails to compile. +- **`v3.3.0-rc2` → `v3.3.0-rc3` is a no-op for this engine.** The only change to the document + surface (`draft-IERC1643.sol`, `IDocumentEngine.sol`, `DocumentEngineModule.sol`, + `DocumentERC1643Module.sol`) is a pragma bump from `^0.8.20` to `^0.8.24`; the interface, the + errors and the `getDocument` return shape are unchanged. - The `IERC1643` import path moved in CMTAT v3, from `CMTAT/interfaces/engine/draft-IERC1643.sol` to `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. - Document names became `bytes32` in CMTAT v3 (they were `string` up to v2.5.0-rc0). diff --git a/erc-1643-proposition.md b/erc-1643-proposition.md new file mode 100644 index 0000000..91398ea --- /dev/null +++ b/erc-1643-proposition.md @@ -0,0 +1,59 @@ +# Null `subject` in multi-subject document management — superseded working note + +> **Superseded.** Everything this note proposed now exists as a full ERC draft: +> [`doc/ERCSpecification/erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md). +> That draft is the normative document; this file is kept only as a record of how the decision was +> reached and where the outcome differs from what was first proposed. Do not cite it as a +> specification. + +## Outcome + +| | First proposed here | Adopted in the draft | +| --- | --- | --- | +| Error name | `ERC1643InvalidSubject()` | **`MultiDocumentInvalidSubject()`** | +| Where declared | ERC-1643's extension section | its own interface, `IERC1643MultiDocument` | +| Normative strength | `SHOULD` revert on `subject == address(0)` | `SHOULD` — unchanged | +| Guarded paths | write path only | write path only — unchanged | +| Security note | one line | a dedicated *The null subject* consideration | + +The implementation follows the draft: `DocumentEngineBase._setDocument` reverts +`MultiDocumentInvalidSubject()`, declared in `src/interfaces/IERC1643MultiDocument.sol`. + +## Why the name changed + +The original proposal reused ERC-1643's prefix for symmetry with `ERC1643InvalidName()` and +`ERC1643MissingDocument()`. The draft rejects that, on the principle that **an error is prefixed by +the proposal that defines its condition, not by the proposal it happens to sit next to**: + +- `ERC1643InvalidName` / `ERC1643MissingDocument` keep their prefix because they genuinely *are* + ERC-1643's errors, reused unchanged so that revert data is identical whether a caller is talking to + an ERC-1643 contract or to a multi-subject manager. Renaming them would fragment that. +- The null-`subject` condition **cannot arise in ERC-1643 at all**: its `setDocument` has no + `subject` argument, so the subject is implicitly the contract itself, which is never the null + address. Borrowing the `ERC1643` prefix would name the error after a standard in which the + condition is unreachable. + +The name is provisional, along with `IERC1643MultiDocument` and `IMultiDocumentSubject`; all three +track the draft's own number once an editor assigns one. The two reused ERC-1643 names are **not** +provisional. + +## What still holds from the original reasoning + +Retained by the draft, and still the justification for the guard: + +- **Not a security hole.** Namespaces are isolated (`_documents[subject][name]`), and `address(0)` + cannot call the contract to read "its own" documents, so a document stored under the null subject + is inert. +- **But a data-integrity issue.** `subject` is by definition the address of the contract the + documents belong to; `address(0)` is never such a contract. Allowing it lets callers populate a + namespace no contract can ever own, cluttering state and misleading off-chain indexers that key on + `subject`. +- **Guarding the write path is sufficient.** With `setDocument` rejecting the null subject, no + document can exist under it, so `removeDocument` there already fails with + `ERC1643MissingDocument()`. No second guard is needed. +- **Precedent.** ERC-20 / ERC-721 reject the zero address on mint and transfer for the same + "never a meaningful participant" reason. + +The same argument is what motivates rejecting `address(0)` in `bindToken` / `unbindToken` +(`src/modules/TokenBindingModule.sol`), which is not a specification requirement but follows from +the identical premise. diff --git a/foundry.lock b/foundry.lock index 8dfec2c..423426c 100644 --- a/foundry.lock +++ b/foundry.lock @@ -1,14 +1,14 @@ { "lib/CMTAT": { "tag": { - "name": "v3.3.0-rc2", - "rev": "35d8940b40943828c5ea407dc6b22d559d92e4ae" + "name": "v3.3.0-rc3", + "rev": "658672f190d56d3f61663a7d6d51962b8980df70" } }, "lib/RuleEngine": { "tag": { - "name": "v3.0.0-rc4", - "rev": "66fcf2aafebd1f9d9de8a81dec92b88da071c9b3" + "name": "v3.0.0-rc5", + "rev": "ab9def2f19ae71af304127f42d20d9831cad1a2b" } }, "lib/forge-std": { @@ -19,14 +19,14 @@ }, "lib/openzeppelin-contracts": { "tag": { - "name": "v5.6.1", - "rev": "5fd1781b1454fd1ef8e722282f86f9293cacf256" + "name": "v5.7.0", + "rev": "cab19933c33c2ad1d4c7a84864a3601dddfd16f3" } }, "lib/openzeppelin-contracts-upgradeable": { "tag": { - "name": "v5.6.1", - "rev": "7bf4727aacdbfaa0f36cbd664654d0c9e1dc52bf" + "name": "v5.7.0", + "rev": "14f52c54d3a1eefbda3d4071efba24d3c1e07e8a" } } } diff --git a/lib/CMTAT b/lib/CMTAT index 580d477..658672f 160000 --- a/lib/CMTAT +++ b/lib/CMTAT @@ -1 +1 @@ -Subproject commit 580d4776e4cbb857b2da7d83fd79144ae7e47557 +Subproject commit 658672f190d56d3f61663a7d6d51962b8980df70 diff --git a/lib/RuleEngine b/lib/RuleEngine index 66fcf2a..ab9def2 160000 --- a/lib/RuleEngine +++ b/lib/RuleEngine @@ -1 +1 @@ -Subproject commit 66fcf2aafebd1f9d9de8a81dec92b88da071c9b3 +Subproject commit ab9def2f19ae71af304127f42d20d9831cad1a2b diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index 5fd1781..cab1993 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit 5fd1781b1454fd1ef8e722282f86f9293cacf256 +Subproject commit cab19933c33c2ad1d4c7a84864a3601dddfd16f3 diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable index 7bf4727..14f52c5 160000 --- a/lib/openzeppelin-contracts-upgradeable +++ b/lib/openzeppelin-contracts-upgradeable @@ -1 +1 @@ -Subproject commit 7bf4727aacdbfaa0f36cbd664654d0c9e1dc52bf +Subproject commit 14f52c54d3a1eefbda3d4071efba24d3c1e07e8a From d3c81bb8934dc22daaa4ce7ab2bccc33ff7926bb Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 10:43:16 +0200 Subject: [PATCH 27/47] =?UTF-8?q?fix:=20raise=20pragma=20to=20^0.8.24=20?= =?UTF-8?q?=E2=80=94=20the=20true=20src=20floor=20after=20CMTAT=20rc3=20an?= =?UTF-8?q?d=20OZ=20v5.7.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 4 + CHANGELOG.md | 8 + CLAUDE.md | 4 + IMPROVEMENT.md | 302 ------------------ README.md | 10 +- .../v0.4.0/aderyn/aderyn-report-feedback.md | 4 +- .../tools/v0.4.0/aderyn/aderyn-report.md | 2 +- erc-1643-proposition.md | 59 ---- script/DeployDocumentEngine.s.sol | 2 +- script/DeployDocumentEngineOwnable.s.sol | 2 +- src/DocumentEngine.sol | 2 +- src/DocumentEngineBase.sol | 2 +- src/DocumentEngineInvariant.sol | 2 +- src/DocumentEngineOwnable.sol | 2 +- src/interfaces/IERC1643MultiDocument.sol | 2 +- src/interfaces/IERC8303.sol | 2 +- src/interfaces/ITokenBinding.sol | 2 +- src/modules/TokenBindingModule.sol | 2 +- src/modules/VersionModule.sol | 2 +- test/Deploy.t.sol | 2 +- test/DocumentEngine.t.sol | 2 +- test/DocumentEngineOwnable.t.sol | 2 +- 22 files changed, 41 insertions(+), 380 deletions(-) delete mode 100644 IMPROVEMENT.md delete mode 100644 erc-1643-proposition.md diff --git a/AGENTS.md b/AGENTS.md index 31f53f0..bffab70 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -140,6 +140,10 @@ change *who* is authorized in a deployment (implement the `_authorize*` hooks). Other important files: - `foundry.toml` — solc `0.8.34`, `evm_version = prague` (required by CMTAT v3). + Sources declare `pragma solidity ^0.8.24` — the real `src/` floor, set by OpenZeppelin's + `AccessControlEnumerable`/`EnumerableSet` and CMTAT's `draft-IERC1643` since `v3.3.0-rc3`. + Building the tests needs `≥ 0.8.27` (CMTAT's `require(cond, CustomError())` is via-ir-only + before then). Keep the pragma honest: if a dependency raises its floor, raise ours to match. - `remappings.txt` — `CMTAT/`, `RuleEngine/`, `OZ/`, `@openzeppelin/contracts-upgradeable/`. - `CHANGELOG.md` — semver history; update on every release (current: `v0.4.0`). - `ERC-1643-proposition.md` — proposed optional multi-token events / extension. diff --git a/CHANGELOG.md b/CHANGELOG.md index f591acb..e17d47c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,14 @@ for which CMTAT release each version of this engine is built against. - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) [`v3.0.0-rc5`](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) - `foundry.lock` now records every submodule by tag; all five entries had gone stale since `v0.3.0`. - **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) +- **Source pragma raised `^0.8.20` → `^0.8.24`** across `src/`, `script/` and `test/`. This is a + correction, not a new restriction: `^0.8.20` had become an over-promise, advertising a range the + sources could not actually compile in. OpenZeppelin's `AccessControlEnumerable.sol` and + `EnumerableSet.sol` are `^0.8.24`, and CMTAT `v3.3.0-rc3` moved `draft-IERC1643.sol` to `^0.8.24` + as well, so every contract in `src/` now transitively requires it — `forge build --use 0.8.23` + fails to resolve a compiler. `0.8.24` is the real `src/` floor; the full project including the + CMTAT-importing tests needs `0.8.27`, because `require(cond, CustomError())` is restricted to the + via-ir pipeline before then. Deployed bytecode is unaffected — the pinned compiler is still `0.8.34`. - **`IERC1643` (CMTAT v3) breaking changes** - `getDocument` keeps returning `(string uri, bytes32 documentHash, uint256 lastModified)` — the flat ERC-1643 ABI — on **both** overloads, `getDocument(bytes32)` and diff --git a/CLAUDE.md b/CLAUDE.md index 31f53f0..bffab70 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -140,6 +140,10 @@ change *who* is authorized in a deployment (implement the `_authorize*` hooks). Other important files: - `foundry.toml` — solc `0.8.34`, `evm_version = prague` (required by CMTAT v3). + Sources declare `pragma solidity ^0.8.24` — the real `src/` floor, set by OpenZeppelin's + `AccessControlEnumerable`/`EnumerableSet` and CMTAT's `draft-IERC1643` since `v3.3.0-rc3`. + Building the tests needs `≥ 0.8.27` (CMTAT's `require(cond, CustomError())` is via-ir-only + before then). Keep the pragma honest: if a dependency raises its floor, raise ours to match. - `remappings.txt` — `CMTAT/`, `RuleEngine/`, `OZ/`, `@openzeppelin/contracts-upgradeable/`. - `CHANGELOG.md` — semver history; update on every release (current: `v0.4.0`). - `ERC-1643-proposition.md` — proposed optional multi-token events / extension. diff --git a/IMPROVEMENT.md b/IMPROVEMENT.md deleted file mode 100644 index 79f65e9..0000000 --- a/IMPROVEMENT.md +++ /dev/null @@ -1,302 +0,0 @@ -# IMPROVEMENT — open items - -Known deviations, gaps and improvement opportunities in `DocumentEngine`, carried forward as of -**`v0.4.0`** (CMTAT `v3.3.0-rc3`). - -They come from a clause-by-clause conformance analysis of the implementation against the two -specifications this engine implements: - -| Spec | File | Role | -| --- | --- | --- | -| ERC-1643 — Document Management for Security Tokens | [`doc/ERCSpecification/erc-1643.md`](./doc/ERCSpecification/erc-1643.md) | Per-contract interface | -| Multi-Subject Document Management (unnumbered draft) | [`doc/ERCSpecification/erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md) | Address-scoped companion interface | - -**None of these is an exploitable vulnerability.** Items already fixed are not repeated here — they -are recorded in [`CHANGELOG.md`](./CHANGELOG.md) and in -[`doc/audits/AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md). Static-analysis findings are -tracked separately under [`doc/audits/tools/`](./doc/audits/tools). - -> This project has not been audited. These items are the output of specification review and -> automated tooling, not of a formal security audit. - -## Summary - -| # | Item | Severity | Effort | Kind | -| --- | --- | --- | --- | --- | -| [1](#1--authorization-granularity-is-fixed-at-compile-time-the-hook-cannot-express-per-subject-rules) | Authorization granularity is fixed at compile time; the hook cannot express per-`subject` rules | Low¹ | Medium | Extensibility | -| [2](#2--the-admin-path-bypasses-subject-side-erc-1643-emission) | Admin path bypasses subject-side ERC-1643 emission | **Medium** | Small–Medium | Spec `SHOULD` | -| [4](#4--enumeration-cost-and-removal-complexity) | Enumeration cost and removal complexity | Low | Medium | Gas | -| [5](#5--the-erc-2771-forwarder-is-a-universal-write-authority) | ERC-2771 forwarder is a universal write authority | Info | Trivial | Docs | -| [6](#6--_removedocument-emits-before-the-state-change) | `_removeDocument` emits before the state change | Info | Trivial | Cosmetic | -| [7](#7--upstream-imultidocumentsubject-manager-discovery) | Upstream: `IMultiDocumentSubject` manager discovery | Info | — | Upstream | - -Item numbers are stable identifiers, referenced from `CHANGELOG.md` and the audit reports; a resolved -item's number is retired rather than reused. **Item 3 was closed in `v0.4.0`** by documenting what -`type(IERC1643).interfaceId` does and does not promise on this address. - -¹ Low for the single-issuer fleet this engine targets, which is the model the draft sets out to -support. **Medium** only for a deployment shared by unrelated issuers — see item 1 for why that -configuration is not supportable today. - -**Item 2 is the most severe open item.** Item 1 is the only one that changes what the contract can -*express*, and it is source-compatible for existing deployments (the default hook bodies would ignore -the new argument), so it need not wait for a breaking release. Everything else is documentation, gas, -or cosmetic. - ---- - -## 1 — Authorization granularity is fixed at compile time; the hook cannot express per-`subject` rules - -**Severity:** Low — **Medium** for a deployment shared across unrelated issuers · **Effort:** Medium -· **Kind:** extensibility + deployment guidance - -**Where:** `src/DocumentEngineBase.sol:56`, `:111-186`; `src/DocumentEngine.sol:46-48`; -`src/DocumentEngineOwnable.sol:39-41` - -### This is not a conformance failure - -The draft's requirement is: - -> Implementations MUST authorize writes per `subject`, so that a caller cannot create, update, or -> remove documents for a `subject` **it is not permitted to manage**. -> — draft §Authorization - -The operative words are "not permitted to manage", and what a caller is permitted to manage is -defined by the deployment's own access control. `DOCUMENT_MANAGER_ROLE` is a specific, granted role -whose permission covers every subject the engine serves — so there is no subject its holder is *not* -permitted to manage, and the clause is satisfied. Same for `owner` in `DocumentEngineOwnable`. - -This is the case the draft explicitly sets out to support: - -> An issuer operating many tokens, funds, or vaults typically maintains one document library and -> **one set of operators**, and duplicating that storage and access-control logic into every subject -> contract is redundant and expensive. -> — draft §Motivation - -A single global operator role over a fleet one issuer controls is that design, not a departure from -it. The draft's test case — "A caller not authorized for a subject failing to create, update, or -remove that subject's documents" — is covered by `testCannotNonAdminSetDocument` and its siblings: an -account without the role is authorized for no subject, and its write reverts. - -### What is actually open - -The draft's Security Consideration is about **unrelated** subjects: - -> If writes are not authorized per `subject`, any caller permitted to write for one subject can -> modify another subject's legal or operational references. - -That bites only when one engine instance is shared by parties that do not trust each other — two -issuers, or a service operator hosting documents for external clients. In that deployment a global -role does breach the property, and **this engine cannot currently express the alternative**, because -the authorization hook receives no subject: - -```solidity -function _authorizeDocumentManagement() internal view virtual; // no subject parameter -``` - -So a deployer cannot subclass their way to per-subject rules; they would have to edit -`DocumentEngineBase`. Two consequences: - -1. **A multi-tenant deployment is not supportable today.** The only conformant option is one engine - instance per trust domain — which is fine, and cheap, but is a deployment constraint that should - be written down rather than discovered. -2. **It contradicts the project's own advertised extension model.** The README and `CLAUDE.md` - promise that a deployment changes *who* is authorized by overriding a hook, "not by editing the - management functions". That holds for swapping roles for an owner; it does not hold for making the - decision depend on the subject. The hook is the documented seam, and this is the one axis it - cannot turn. - -Related detail, relevant only if the hook ever gains a subject: in the batch functions the modifier -fires **once** for the whole call, before any element is read, so a subject-aware hook would have to -be invoked inside the loops rather than via the modifier. - -### Recommendation - -Low priority, and **not** required for the single-issuer model this engine targets. Two options: - -- *Documentation only* (sufficient today): state in the README that one engine instance serves one - trust domain, and that unrelated issuers should each deploy their own rather than share one. -- *Enable the axis*, if multi-tenant support is ever wanted: - - ```solidity - function _authorizeDocumentManagement(address subject) internal view virtual; - - function batchSetDocuments(address[] calldata subjects, ...) external { - for (uint256 i = 0; i < length; ++i) { - _authorizeDocumentManagement(subjects[i]); - _setDocument(subjects[i], names[i], uris[i], hashes[i]); - } - } - ``` - - The default implementations stay exactly as they are (`_checkRole(DOCUMENT_MANAGER_ROLE)` / - `_checkOwner()`, ignoring `subject`), so behaviour and gas are unchanged and no existing deployment - is affected — a subclass simply gains the option of a per-subject rule such as - `keccak256("DOCUMENT_MANAGER", subject)`. Token binding would need a separate hook - (`_authorizeTokenBinding()`), since binding has no subject. - -Note also that the bound-token path is already per-subject and cannot be escaped: the namespace is -`_msgSender()`, structurally. A subject that manages its own documents is unaffected by any of this. - -## 2 — The admin path bypasses subject-side ERC-1643 emission - -**Severity:** Medium · **Effort:** Small–Medium · **Kind:** deviation from a specification `SHOULD` - -**Where:** `src/DocumentEngineBase.sol:71-84`; demonstrated at `test/DocumentEngine.t.sol:197` - -> A deployment in which an operator calls `setDocument(address subject, ...)` … directly, with no -> execution point in the subject, does **not** satisfy ERC-1643's emission requirement for that -> subject. -> — draft §Call Topology - -> a management contract SHOULD restrict its address-scoped writes to callers for which one of the two -> topologies above holds. - -Neither deployment applies such a restriction. The repository's own CMTAT integration test writes -through exactly the prohibited path: - -```solidity -// test/DocumentEngine.t.sol:196-197 -vm.prank(admin); -documentEngine.setDocument(address(cmtat), documentName, documentURI, documentHash); -``` - -The engine emits `DocumentUpdatedForSubject`; the CMTAT token emits nothing; anyone subscribed to the -token's address concludes its documents are unchanged. The failure is silent in both directions, as -the draft's Security Considerations describe. - -The **subject-initiated** topology, by contrast, became fully conformant with CMTAT `v3.3.0-rc2`, -which lists this under *Fixed* as "Delegating document token emits ERC-1643 events on its own address -(dual emission)". `DocumentEngineModule` forwards to the engine and then re-emits `DocumentUpdated` / -`DocumentRemoved` on the **token's** own address (`DocumentEngineModule.sol:91-92`, `:102-104`), -reading the metadata before removal so `DocumentRemoved` carries the removed values as the spec -requires; the engine emits the address-carrying events on its own address. Both mutators also revert -with `CMTAT_DocumentEngineModule_NoDocumentEngine` when no engine is set, closing a path where a -write would previously have been lost. - -So the gap is narrow but sharp: whether a CMTAT subject's documents are observable on its own address -depends entirely on **which door the operator uses**. Through the token (`cmtat.setDocument(...)`) it -is; straight to the engine (`engine.setDocument(address(cmtat), ...)`) it is not. Nothing in either -contract signals the difference. - -**Recommendation.** Pick one and state it: - -- *Documentation-only* (cheapest): a prominent README/NatSpec note that the address-scoped writes are - for subjects that either do not implement ERC-1643 or accept the loss of per-contract - observability; the bound-token path is the conformant route for ERC-1643 subjects. -- *Enforced*: gate the address-scoped writes on `isTokenBound(subject) == false`, forcing ERC-1643 - subjects through their own contract. -- *Callback*: add the draft's "manager-initiated with callback" topology — an optional permissioned - hook on the subject invoked after the write, so the subject emits. - -## 4 — Enumeration cost and removal complexity - -**Severity:** Low · **Effort:** Medium · **Kind:** gas / scalability - -**Where:** `src/DocumentEngineBase.sol:236-245`, `:206-216`, `:150-186` - -- `_removeDocumentName` is a linear scan over the subject's name array. The draft's Reference - Implementation section expects "index tracking to support O(1) removals". `batchRemoveDocuments` - compounds this to O(n·m) and can plausibly exceed the block gas limit for a subject with a large - document set. -- `getAllDocuments` returns the entire array with no paginated alternative. ERC-1643's Security - Considerations explicitly call this out: "Implementations expecting large sets should consider - exposing an additional paginated accessor alongside this interface." - -Neither is a conformance failure. Independently corroborated by Aderyn — L-5 at -`DocumentEngineBase.sol:238`, reached from its "costly operation in a loop" heuristic -([triage](./doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md)). - -**Recommendation.** Add `mapping(address => mapping(bytes32 => uint256)) private _nameIndex` for -O(1) swap-and-pop, plus `getDocumentsPaginated(address subject, uint256 offset, uint256 limit)` and -`getDocumentCount(address subject)`. - -## 5 — The ERC-2771 forwarder is a universal write authority - -**Severity:** Info · **Effort:** Trivial · **Kind:** documentation - -**Where:** `src/DocumentEngine.sol:99-101`; `src/modules/TokenBindingModule.sol:84-88` - -`_msgSender()` drives both `_checkTokenBound()` and the role check. The trusted forwarder can -therefore present itself as any bound subject — writing into that subject's namespace — and as any -role holder. This is the ordinary ERC-2771 trust assumption, but it deserves stating explicitly given -the draft's framing: - -> Subjects should treat the choice of management contract as a permissioning decision, not merely a -> storage one. - -A subject binding to this engine is also trusting the engine's forwarder. `forwarderIrrevocable` is -immutable, which is the right call for predictability, but it also means a compromised forwarder -cannot be revoked — the only remedy is unbinding every subject and migrating. - -**Recommendation.** Document the forwarder as part of every bound subject's trust boundary, in the -README section on ERC-2771 and in the constructor NatSpec. - -## 6 — `_removeDocument` emits before the state change - -**Severity:** Info · **Effort:** Trivial · **Kind:** cosmetic - -**Where:** `src/DocumentEngineBase.sol:258` (emit) before `:260-261` (delete) - -The specs mandate "after state changes" for `setDocument` only, and the implementation complies there -(`:286`). For removal the metadata must be read before deletion, so the current ordering is -convenient; there are no external calls anywhere in the write path, so there is no reentrancy -exposure. - -**Recommendation.** Cosmetic only — the metadata is already cached in the `doc` local, so emitting -after the delete would align both paths at no cost. - -## 7 — Upstream: `IMultiDocumentSubject` manager discovery - -**Severity:** Info · **Kind:** upstream (CMTAT), not this repository - -Not implemented here, and correctly so: the draft is explicit that this interface is "**implemented -by the subject, not by the management contract**", and it is optional for both. No action is required -of the engine. - -There is, however, a near-miss upstream worth aligning. CMTAT's `DocumentEngineModule` already -exposes the same concept under a different shape: - -| draft `IMultiDocumentSubject` | CMTAT `DocumentEngineModule` | -| --- | --- | -| `documentManager() returns (address)` | `documentEngine() returns (IERC1643)` | -| `DocumentManagerUpdated(address previous, address new)` | `DocumentEngine(IERC1643 engine)` — no previous address | -| MUST return `address(0)` when not delegated | ✅ default zero | -| MUST NOT revert | ✅ | - -**Recommendation.** Adding the previous address to the CMTAT event, or an alias getter, would make -CMTAT tokens discoverable by any consumer implementing the draft. That is a change for CMTAT, not for -this repository. - ---- - -## Not open items - -Recorded so they are not re-raised. Verified conformant, several by explicit test: - -- **Subject isolation** — `mapping(address => mapping(bytes32 => Document))` with a per-subject name - array, covered by a fuzz test. A bound subject writes to `_msgSender()` and structurally cannot - reach another namespace. -- **Emission responsibility** — only `DocumentUpdatedForSubject` / `DocumentRemovedForSubject`, never - the base events, as the draft requires of a multi-subject manager. The suite asserts the *absence* - of the base events. -- **`lastModified == 0` as the sole absent-entry sentinel** — respected as both the read convention - and the internal existence check, keeping `uri` / `documentHash` free to be legitimately empty. -- **Null-subject guard on the write path only** — matching the draft's note that guarding - `setDocument` suffices, since removal then fails with `ERC1643MissingDocument()` anyway. -- **`IERC1643MultiDocument` neither inherits nor imports `IERC1643`** — the independence the draft - requires is a property of the file, not a convention. -- **`getDocument` returns the flat ERC-1643 ABI** — pinned by a test that inspects returndata, since - the interface id is identical for both shapes and ERC-165 cannot catch a regression. Both interface - ids are asserted as literals (`0xecfecec8`, `0xa2b1179b`). -- **Unstable ordering after removal** — explicitly permitted by ERC-1643; swap-and-pop is fine. -- **ERC-165 advertises `type(IERC1643).interfaceId`** (was item 3, closed in `v0.4.0` by - documentation, which was the original recommendation). The base single-argument functions exist, - which is what the draft conditions the id on, and a **token** uses the advertisement to confirm - those endpoints before wiring itself to the engine. The caveat it does *not* cover — those - functions are `_msgSender()`-scoped, so a third party reading `getDocument(name)` from the engine - gets its own empty namespace instead of the subject's documents, silently — is now stated in the - README, in the `supportsInterface` NatSpec of both deployments, and asserted by - `testBaseERC1643IsAdvertisedButReadsAreCallerScoped`. Briefly removed during `v0.4.0` development - and restored: dropping the id would have made a token's legitimate capability check fail in order - to discourage a misuse that documentation addresses directly. diff --git a/README.md b/README.md index 525818e..49d2d55 100644 --- a/README.md +++ b/README.md @@ -361,8 +361,14 @@ Notes on the CMTAT v2 → v3 jump at `v0.4.0`: - The `IERC1643` import path moved in CMTAT v3, from `CMTAT/interfaces/engine/draft-IERC1643.sol` to `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. - Document names became `bytes32` in CMTAT v3 (they were `string` up to v2.5.0-rc0). -- Solidity `≥ 0.8.27` is required from `v0.4.0` on, because CMTAT v3 uses - `require(cond, CustomError())`. +- Two different Solidity floors apply from `v0.4.0` on, and the sources declare the lower of them: + - **`src/` requires `≥ 0.8.24`** — the pragma every file declares. OpenZeppelin's + `AccessControlEnumerable.sol` / `EnumerableSet.sol` and, since CMTAT `v3.3.0-rc3`, + `draft-IERC1643.sol` are all `^0.8.24`, so no contract here compiles below it. + - **Building the full project, tests included, requires `≥ 0.8.27`**, because CMTAT v3 uses + `require(cond, CustomError())`, which is restricted to the via-ir pipeline before `0.8.27`. + + This is why the declared pragma is `^0.8.24` while `foundry.toml` pins `0.8.34`. Exact submodule revisions are pinned in [`foundry.lock`](./foundry.lock). diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md index 3bfe2fb..3b7a940 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md @@ -31,8 +31,8 @@ CMTAT upgrade. | ID | Detector | Sev | Instances | Disposition | Reason (verified against the cited lines) | | --- | --- | --- | --- | --- | --- | | L-1 | Centralization Risk | Low | 2 | **By design** | `DocumentEngine.sol:24`, `DocumentEngineOwnable.sol:24`. The whole premise of the contract is that a trusted operator manages documents for a fleet of subjects; `DOCUMENT_MANAGER_ROLE` (and `owner`) are that operator. Documented in the README and analysed in `IMPROVEMENT.md` item 1, which concludes the global role is the correct model for the single-issuer fleet this engine targets. Aderyn cannot express that distinction. | -| L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** | Every file uses `pragma solidity ^0.8.20;`. The caret is intentional so the sources stay consumable as a library by projects on a different `0.8.x`; the compiler actually used for the deployed bytecode is pinned to `0.8.34` in `foundry.toml`, and `foundry.lock` pins every dependency. Verified: no file uses a construct that behaves differently across the allowed range. | -| L-3 | PUSH0 Opcode | Low | 9 | **Environment** | Consequence of `^0.8.20` plus `evm_version = prague`: the compiler emits `PUSH0`, which is unavailable on chains that have not adopted Shanghai. Not a source defect. A deployer targeting such a chain must lower `evm_version` in `foundry.toml` — but CMTAT v3 itself requires `prague`, so that configuration is out of scope for this engine. | +| L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** (floor since raised) | Every file uses a caret pragma, intentionally, so the sources stay consumable as a library by projects on a different `0.8.x`; the compiler actually used for the deployed bytecode is pinned to `0.8.34` in `foundry.toml`, and `foundry.lock` pins every dependency. Verified: no file uses a construct that behaves differently across the allowed range. **Update (post-run):** the floor this report saw, `^0.8.20`, over-promised once CMTAT `v3.3.0-rc3` moved `draft-IERC1643.sol` to `^0.8.24` — `0.8.20`–`0.8.23` could not in fact compile the tree (`AccessControlEnumerable.sol` and `EnumerableSet.sol` were already `^0.8.24`). Every file is now `^0.8.24`, which is the true `src/` floor; the full project including the CMTAT-importing tests needs `0.8.27`, because `require(cond, CustomError())` is legacy-pipeline-only from that version on. | +| L-3 | PUSH0 Opcode | Low | 9 | **Environment** | Consequence of the caret pragma plus `evm_version = prague`: the compiler emits `PUSH0`, which is unavailable on chains that have not adopted Shanghai. Not a source defect. A deployer targeting such a chain must lower `evm_version` in `foundry.toml` — but CMTAT v3 itself requires `prague`, so that configuration is out of scope for this engine. | | L-4 | Loop Contains `require`/`revert` | Low | 4 | **By design** | `DocumentEngineBase.sol:124, 142, 156, 170` — the four batch loops. The reverts are raised inside `_setDocument` / `_removeDocument` (`ERC1643InvalidName`, `MultiDocumentInvalidSubject`, `ERC1643MissingDocument`). Batch operations are deliberately **all-or-nothing**: a batch containing one bad entry must not half-apply, since partial application would leave the operator unable to tell which documents were written without re-reading every entry. Skipping bad entries instead would silently drop them. | | L-5 | Costly operations inside loop | Low | 5 | **By design** ×4, **known item** ×1 | Four instances (`:124, 142, 156, 170`) are storage writes in the batch loops — unavoidable, and the reason the batch functions exist is to amortise the 21 000-gas transaction overhead across those writes. The fifth (`:238`) is `_removeDocumentName`'s linear scan with swap-and-pop; see the triage note above and `IMPROVEMENT.md` item 4. | | L-6 | Unchecked Return | Low | 1 | **False positive** | `DocumentEngine.sol:35`, `_grantRole(DEFAULT_ADMIN_ROLE, admin);`. OpenZeppelin's `_grantRole` returns `false` only when the account already holds the role. This call is in the constructor of a freshly deployed contract, where no role has been granted yet, so it always returns `true`; `admin == address(0)` is already rejected on the preceding lines. There is no state to check and no recovery path to take. | diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md index 0044169..a47629d 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md @@ -11,7 +11,7 @@ > | ID | Detector | Sev | Instances | Assessment | > | --- | --- | --- | --- | --- | > | L-1 | Centralization Risk | Low | 2 | **By design** — a document manager is a privileged operator by definition | -> | L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** — `^0.8.20` is deliberate; the deployed compiler is pinned in `foundry.toml` | +> | L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** — the caret is deliberate; the deployed compiler is pinned in `foundry.toml`. The `^0.8.20` floor seen by this run has since been raised to `^0.8.24` — see the feedback file | > | L-3 | PUSH0 Opcode | Low | 9 | **Environment** — `evm_version = prague`; only relevant on chains without PUSH0 | > | L-4 | Loop Contains `require`/`revert` | Low | 4 | **By design** — batch operations are deliberately all-or-nothing | > | L-5 | Costly operations inside loop | Low | 5 | **By design** (4 batch loops) + **1 known item** — `_removeDocumentName` is O(n), see §4.7 | diff --git a/erc-1643-proposition.md b/erc-1643-proposition.md deleted file mode 100644 index 91398ea..0000000 --- a/erc-1643-proposition.md +++ /dev/null @@ -1,59 +0,0 @@ -# Null `subject` in multi-subject document management — superseded working note - -> **Superseded.** Everything this note proposed now exists as a full ERC draft: -> [`doc/ERCSpecification/erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md). -> That draft is the normative document; this file is kept only as a record of how the decision was -> reached and where the outcome differs from what was first proposed. Do not cite it as a -> specification. - -## Outcome - -| | First proposed here | Adopted in the draft | -| --- | --- | --- | -| Error name | `ERC1643InvalidSubject()` | **`MultiDocumentInvalidSubject()`** | -| Where declared | ERC-1643's extension section | its own interface, `IERC1643MultiDocument` | -| Normative strength | `SHOULD` revert on `subject == address(0)` | `SHOULD` — unchanged | -| Guarded paths | write path only | write path only — unchanged | -| Security note | one line | a dedicated *The null subject* consideration | - -The implementation follows the draft: `DocumentEngineBase._setDocument` reverts -`MultiDocumentInvalidSubject()`, declared in `src/interfaces/IERC1643MultiDocument.sol`. - -## Why the name changed - -The original proposal reused ERC-1643's prefix for symmetry with `ERC1643InvalidName()` and -`ERC1643MissingDocument()`. The draft rejects that, on the principle that **an error is prefixed by -the proposal that defines its condition, not by the proposal it happens to sit next to**: - -- `ERC1643InvalidName` / `ERC1643MissingDocument` keep their prefix because they genuinely *are* - ERC-1643's errors, reused unchanged so that revert data is identical whether a caller is talking to - an ERC-1643 contract or to a multi-subject manager. Renaming them would fragment that. -- The null-`subject` condition **cannot arise in ERC-1643 at all**: its `setDocument` has no - `subject` argument, so the subject is implicitly the contract itself, which is never the null - address. Borrowing the `ERC1643` prefix would name the error after a standard in which the - condition is unreachable. - -The name is provisional, along with `IERC1643MultiDocument` and `IMultiDocumentSubject`; all three -track the draft's own number once an editor assigns one. The two reused ERC-1643 names are **not** -provisional. - -## What still holds from the original reasoning - -Retained by the draft, and still the justification for the guard: - -- **Not a security hole.** Namespaces are isolated (`_documents[subject][name]`), and `address(0)` - cannot call the contract to read "its own" documents, so a document stored under the null subject - is inert. -- **But a data-integrity issue.** `subject` is by definition the address of the contract the - documents belong to; `address(0)` is never such a contract. Allowing it lets callers populate a - namespace no contract can ever own, cluttering state and misleading off-chain indexers that key on - `subject`. -- **Guarding the write path is sufficient.** With `setDocument` rejecting the null subject, no - document can exist under it, so `removeDocument` there already fails with - `ERC1643MissingDocument()`. No second guard is needed. -- **Precedent.** ERC-20 / ERC-721 reject the zero address on mint and transfer for the same - "never a meaningful participant" reason. - -The same argument is what motivates rejecting `address(0)` in `bindToken` / `unbindToken` -(`src/modules/TokenBindingModule.sol`), which is not a specification requirement but follows from -the identical premise. diff --git a/script/DeployDocumentEngine.s.sol b/script/DeployDocumentEngine.s.sol index d76e75f..f99b714 100644 --- a/script/DeployDocumentEngine.s.sol +++ b/script/DeployDocumentEngine.s.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: MPL-2.0 -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; import "forge-std/Script.sol"; import {DocumentEngine} from "../src/DocumentEngine.sol"; diff --git a/script/DeployDocumentEngineOwnable.s.sol b/script/DeployDocumentEngineOwnable.s.sol index e7b7e92..67c9a2b 100644 --- a/script/DeployDocumentEngineOwnable.s.sol +++ b/script/DeployDocumentEngineOwnable.s.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: MPL-2.0 -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; import "forge-std/Script.sol"; import {DocumentEngineOwnable} from "../src/DocumentEngineOwnable.sol"; diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index 5cfdff8..2eb6406 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: MPL-2.0 -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; import "OZ/access/extensions/AccessControlEnumerable.sol"; import {IAccessControl} from "OZ/access/IAccessControl.sol"; diff --git a/src/DocumentEngineBase.sol b/src/DocumentEngineBase.sol index 018b563..6dd9f4c 100644 --- a/src/DocumentEngineBase.sol +++ b/src/DocumentEngineBase.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: MPL-2.0 -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; import "OZ/utils/Context.sol"; import "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; diff --git a/src/DocumentEngineInvariant.sol b/src/DocumentEngineInvariant.sol index 81e718b..250ee9d 100644 --- a/src/DocumentEngineInvariant.sol +++ b/src/DocumentEngineInvariant.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: MPL-2.0 -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; /** * @title DocumentEngineInvariant diff --git a/src/DocumentEngineOwnable.sol b/src/DocumentEngineOwnable.sol index c6ce4a1..4a6ca1c 100644 --- a/src/DocumentEngineOwnable.sol +++ b/src/DocumentEngineOwnable.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: MPL-2.0 -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; import {Ownable} from "OZ/access/Ownable.sol"; import {Ownable2Step} from "OZ/access/Ownable2Step.sol"; diff --git a/src/interfaces/IERC1643MultiDocument.sol b/src/interfaces/IERC1643MultiDocument.sol index 9be7e28..b6d0349 100644 --- a/src/interfaces/IERC1643MultiDocument.sol +++ b/src/interfaces/IERC1643MultiDocument.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MPL-2.0 -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; /** * @title IERC1643MultiDocument — optional multi-token ERC-1643 extension diff --git a/src/interfaces/IERC8303.sol b/src/interfaces/IERC8303.sol index a52a77c..4b6194c 100644 --- a/src/interfaces/IERC8303.sol +++ b/src/interfaces/IERC8303.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MPL-2.0 -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; /** * @title IERC8303 - Contract Version diff --git a/src/interfaces/ITokenBinding.sol b/src/interfaces/ITokenBinding.sol index 67cb940..b9c46b9 100644 --- a/src/interfaces/ITokenBinding.sol +++ b/src/interfaces/ITokenBinding.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MPL-2.0 -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; /** * @title ITokenBinding diff --git a/src/modules/TokenBindingModule.sol b/src/modules/TokenBindingModule.sol index a65596b..dfc86e3 100644 --- a/src/modules/TokenBindingModule.sol +++ b/src/modules/TokenBindingModule.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MPL-2.0 -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; import {DocumentEngineBase} from "../DocumentEngineBase.sol"; import {ITokenBinding} from "../interfaces/ITokenBinding.sol"; diff --git a/src/modules/VersionModule.sol b/src/modules/VersionModule.sol index e0957f6..e331d0f 100644 --- a/src/modules/VersionModule.sol +++ b/src/modules/VersionModule.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MPL-2.0 -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; import {ERC165} from "OZ/utils/introspection/ERC165.sol"; import {IERC8303} from "../interfaces/IERC8303.sol"; diff --git a/test/Deploy.t.sol b/test/Deploy.t.sol index 815f1cd..6ad3cdd 100644 --- a/test/Deploy.t.sol +++ b/test/Deploy.t.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: MPL-2.0 -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; import "forge-std/Test.sol"; import {DeployDocumentEngine} from "../script/DeployDocumentEngine.s.sol"; diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index fc3e15a..96b7b94 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: MPL-2.0 -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; import "forge-std/Test.sol"; import "../src/DocumentEngine.sol"; diff --git a/test/DocumentEngineOwnable.t.sol b/test/DocumentEngineOwnable.t.sol index b53e7ae..0cb2049 100644 --- a/test/DocumentEngineOwnable.t.sol +++ b/test/DocumentEngineOwnable.t.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: MPL-2.0 -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; import "forge-std/Test.sol"; import "../src/DocumentEngineOwnable.sol"; From f6ddbe3c727ccef1c30ca808028c842d8bc7d7dd Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 10:55:33 +0200 Subject: [PATCH 28/47] Update surya report --- AGENTS.md | 6 +- CHANGELOG.md | 10 +- CLAUDE.md | 6 +- README.md | 148 ++++++++++++++---- .../surya_graph_DocumentEngine.sol.png | Bin 210056 -> 118324 bytes .../surya_graph_DocumentEngineBase.sol.png | Bin 0 -> 154518 bytes ...urya_graph_DocumentEngineInvariant.sol.png | Bin 10721 -> 13934 bytes .../surya_graph_DocumentEngineOwnable.sol.png | Bin 0 -> 87312 bytes .../surya_graph_IERC1643MultiDocument.sol.png | Bin 0 -> 42431 bytes .../surya_graph/surya_graph_IERC8303.sol.png | Bin 0 -> 15807 bytes .../surya_graph_ITokenBinding.sol.png | Bin 0 -> 26407 bytes .../surya_graph_TokenBindingModule.sol.png | Bin 0 -> 91427 bytes .../surya_graph_VersionModule.sol.png | Bin 0 -> 30796 bytes .../surya_inheritance_DocumentEngine.sol.png | Bin 29866 -> 30983 bytes ...rya_inheritance_DocumentEngineBase.sol.png | Bin 0 -> 29985 bytes ..._inheritance_DocumentEngineOwnable.sol.png | Bin 0 -> 31633 bytes ..._inheritance_IERC1643MultiDocument.sol.png | Bin 0 -> 7270 bytes .../surya_inheritance_IERC8303.sol.png | Bin 0 -> 4282 bytes .../surya_inheritance_ITokenBinding.sol.png | Bin 0 -> 5094 bytes ...rya_inheritance_TokenBindingModule.sol.png | Bin 0 -> 19033 bytes .../surya_inheritance_VersionModule.sol.png | Bin 0 -> 14644 bytes .../surya_report_DocumentEngine.sol.md | 20 +-- .../surya_report_DocumentEngineBase.sol.md | 44 ++++++ ...urya_report_DocumentEngineInvariant.sol.md | 2 +- .../surya_report_DocumentEngineOwnable.sol.md | 32 ++++ .../surya_report_IERC1643MultiDocument.sol.md | 30 ++++ .../surya_report/surya_report_IERC8303.sol.md | 27 ++++ .../surya_report_ITokenBinding.sol.md | 29 ++++ .../surya_report_TokenBindingModule.sol.md | 32 ++++ .../surya_report_VersionModule.sol.md | 28 ++++ 30 files changed, 366 insertions(+), 48 deletions(-) create mode 100644 doc/surya/surya_graph/surya_graph_DocumentEngineBase.sol.png create mode 100644 doc/surya/surya_graph/surya_graph_DocumentEngineOwnable.sol.png create mode 100644 doc/surya/surya_graph/surya_graph_IERC1643MultiDocument.sol.png create mode 100644 doc/surya/surya_graph/surya_graph_IERC8303.sol.png create mode 100644 doc/surya/surya_graph/surya_graph_ITokenBinding.sol.png create mode 100644 doc/surya/surya_graph/surya_graph_TokenBindingModule.sol.png create mode 100644 doc/surya/surya_graph/surya_graph_VersionModule.sol.png create mode 100644 doc/surya/surya_inheritance/surya_inheritance_DocumentEngineBase.sol.png create mode 100644 doc/surya/surya_inheritance/surya_inheritance_DocumentEngineOwnable.sol.png create mode 100644 doc/surya/surya_inheritance/surya_inheritance_IERC1643MultiDocument.sol.png create mode 100644 doc/surya/surya_inheritance/surya_inheritance_IERC8303.sol.png create mode 100644 doc/surya/surya_inheritance/surya_inheritance_ITokenBinding.sol.png create mode 100644 doc/surya/surya_inheritance/surya_inheritance_TokenBindingModule.sol.png create mode 100644 doc/surya/surya_inheritance/surya_inheritance_VersionModule.sol.png create mode 100644 doc/surya/surya_report/surya_report_DocumentEngineBase.sol.md create mode 100644 doc/surya/surya_report/surya_report_DocumentEngineOwnable.sol.md create mode 100644 doc/surya/surya_report/surya_report_IERC1643MultiDocument.sol.md create mode 100644 doc/surya/surya_report/surya_report_IERC8303.sol.md create mode 100644 doc/surya/surya_report/surya_report_ITokenBinding.sol.md create mode 100644 doc/surya/surya_report/surya_report_TokenBindingModule.sol.md create mode 100644 doc/surya/surya_report/surya_report_VersionModule.sol.md diff --git a/AGENTS.md b/AGENTS.md index bffab70..f2bf9d7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -148,7 +148,11 @@ Other important files: - `CHANGELOG.md` — semver history; update on every release (current: `v0.4.0`). - `ERC-1643-proposition.md` — proposed optional multi-token events / extension. - `README.md` — full documentation and Surya schema. -- `doc/` — Surya diagrams/reports (`doc/script/`), coverage, and +- `doc/` — Surya output in `doc/surya/{surya_graph,surya_inheritance,surya_report}`, one file per + `.sol` in `src/` (9 each), regenerated by the three scripts in `doc/script/` — run them from that + directory, **graph first** (it creates the scratch `docOut/`; the report script's `mkdir` lacks + `-p`). Patch `surya/lib/graph.js` before regenerating or every contract calling `super.()` + yields a silent 0-byte PNG; see the Surya section in `README.md`. Also coverage, and `doc/audits/` — the security overview (`AUDIT_OVERVIEW.md`) plus versioned static-analysis output under `doc/audits/tools/vX.Y.Z//`, each with a `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging diff --git a/CHANGELOG.md b/CHANGELOG.md index e17d47c..346d315 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,7 +61,15 @@ for which CMTAT release each version of this engine is built against. rc3 are interchangeable for this engine — between them, the whole document surface (`draft-IERC1643.sol`, `IDocumentEngine.sol`, `DocumentEngineModule.sol`, `DocumentERC1643Module.sol`) changed only its pragma, `^0.8.20` → `^0.8.24`. - - Upgrade OpenZeppelin Contracts (and Contracts Upgradeable) `v5.0.2` → [`v5.7.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.7.0) + - Upgrade OpenZeppelin Contracts (and Contracts Upgradeable) `v5.0.2` → [`v5.7.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.7.0). + `v5.7.0` deprecates `EnumerableSet.at()` in favour of `pos()` (the old name clashes with a + keyword scheduled for Solidity); `at()` remains as a forwarding alias, and this engine has no + call sites either way. The only exposure is inherited — `AccessControlEnumerable.getRoleMember` + switched to `pos()` internally, with no change to its signature, selector or behaviour. + Verified: `DocumentEngine`'s runtime code is **byte-identical** across `v5.6.1` and `v5.7.0` + (8436 bytes; only the CBOR metadata trailer moves, because the source text of + `AccessControlEnumerable.sol` changed), and `DocumentEngineOwnable`'s bytecode is unchanged + including metadata. - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) [`v3.0.0-rc5`](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) - `foundry.lock` now records every submodule by tag; all five entries had gone stale since `v0.3.0`. - **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) diff --git a/CLAUDE.md b/CLAUDE.md index bffab70..f2bf9d7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -148,7 +148,11 @@ Other important files: - `CHANGELOG.md` — semver history; update on every release (current: `v0.4.0`). - `ERC-1643-proposition.md` — proposed optional multi-token events / extension. - `README.md` — full documentation and Surya schema. -- `doc/` — Surya diagrams/reports (`doc/script/`), coverage, and +- `doc/` — Surya output in `doc/surya/{surya_graph,surya_inheritance,surya_report}`, one file per + `.sol` in `src/` (9 each), regenerated by the three scripts in `doc/script/` — run them from that + directory, **graph first** (it creates the scratch `docOut/`; the report script's `mkdir` lacks + `-p`). Patch `surya/lib/graph.js` before regenerating or every contract calling `super.()` + yields a silent 0-byte PNG; see the Surya section in `README.md`. Also coverage, and `doc/audits/` — the security overview (`AUDIT_OVERVIEW.md`) plus versioned static-analysis output under `doc/audits/tools/vX.Y.Z//`, each with a `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging diff --git a/README.md b/README.md index 49d2d55..9aa13c2 100644 --- a/README.md +++ b/README.md @@ -166,8 +166,8 @@ This engine is a **shared, multi-token** document manager, so — per the ERC-16 which token contract a change belongs to; they are the responsibility of the token contract that exposes ERC-1643 to consumers (it re-emits them when delegating). See -[ERC-1643-proposition.md](./doc/ERCSpecification/ERC-1643-proposition.md) and the -`IERC1643MultiDocument` extension. +the [Multi-Subject Document Management draft](./doc/ERCSpecification/erc-draft_multi_document_management.md) +and the `IERC1643MultiDocument` extension. ### Integration with CMTAT @@ -261,47 +261,119 @@ through ERC-165 that those endpoints exist here rather than discovering it from ## Schema +Generated with Surya — regenerate with the three scripts in [`doc/script`](./doc/script). Diagrams +for **every** file in `src/`, interfaces included, live under [`doc/surya`](./doc/surya); the ones +below are the two deployments and the base they share. + ### Inheritance +Both deployments sit on the same two modules — `DocumentEngineBase` (document logic) and +`TokenBindingModule` (the binding allowlist) — and differ only in the access-control layer. + +#### `DocumentEngine` — role-based (`AccessControlEnumerable`) + ![surya_inheritance_DocumentEngine.sol](./doc/surya/surya_inheritance/surya_inheritance_DocumentEngine.sol.png) +#### `DocumentEngineOwnable` — single owner (`Ownable2Step`) +![surya_inheritance_DocumentEngineOwnable.sol](./doc/surya/surya_inheritance/surya_inheritance_DocumentEngineOwnable.sol.png) ### Graph -![surya_graph_DocumentEngine.sol](./doc/surya/surya_graph/surya_graph_DocumentEngine.sol.png) +#### `DocumentEngineBase` — the shared document logic + +![surya_graph_DocumentEngineBase.sol](./doc/surya/surya_graph/surya_graph_DocumentEngineBase.sol.png) +#### `DocumentEngine` +![surya_graph_DocumentEngine.sol](./doc/surya/surya_graph/surya_graph_DocumentEngine.sol.png) -![surya_graph_DocumentEngineInvariant.sol](./doc/surya/surya_graph/surya_graph_DocumentEngineInvariant.sol.png) +#### `DocumentEngineOwnable` + +![surya_graph_DocumentEngineOwnable.sol](./doc/surya/surya_graph/surya_graph_DocumentEngineOwnable.sol.png) ## Surya Description Report ### Contracts Description Table -| Contract | Type | Bases | | | -| :----------------: | :------------------: | :----------------------------------------------: | :------------: | :-----------: | -| └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | -| | | | | | -| **DocumentEngine** | Implementation | DocumentEngineBase, VersionModule, AccessControlEnumerable, ERC2771Context | | | -| └ | | Public ❗️ | 🛑 | NO❗️ | -| └ | setDocument | Public ❗️ | 🛑 | onlyDocumentManager | -| └ | removeDocument | External ❗️ | 🛑 | onlyDocumentManager | -| └ | setDocument | External ❗️ | 🛑 | onlyBoundToken | -| └ | removeDocument | External ❗️ | 🛑 | onlyBoundToken | -| └ | batchSetDocuments | External ❗️ | 🛑 | onlyDocumentManager | -| └ | batchSetDocuments | External ❗️ | 🛑 | onlyDocumentManager | -| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyDocumentManager | -| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyDocumentManager | -| └ | getDocument | External ❗️ | | NO❗️ | -| └ | getDocument | External ❗️ | | NO❗️ | -| └ | getAllDocuments | External ❗️ | | NO❗️ | -| └ | getAllDocuments | External ❗️ | | NO❗️ | -| └ | hasRole | Public ❗️ | | NO❗️ | -| └ | _getDocument | Internal 🔒 | | | -| └ | _removeDocumentName | Internal 🔒 | 🛑 | | -| └ | _removeDocument | Internal 🔒 | 🛑 | | -| └ | _setDocument | Internal 🔒 | 🛑 | | +Per-file reports live in [`doc/surya/surya_report`](./doc/surya/surya_report); the tables below merge +them. Note that the document functions belong to **`DocumentEngineBase`**, not to either deployment — +each deployment contributes only its access-control layer and its ERC-2771 context overrides. + +| Contract | Type | Bases | | | +|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| +| └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | +|||||| +| **DocumentEngineBase** | Implementation | IERC1643, IERC1643MultiDocument, DocumentEngineInvariant, Context ||| +| └ | _authorizeDocumentManagement | Internal 🔒 | | | +| └ | _authorizeBoundTokenDocumentManagement | Internal 🔒 | | | +| └ | setDocument | Public ❗️ | 🛑 | onlyDocumentManager | +| └ | removeDocument | External ❗️ | 🛑 | onlyDocumentManager | +| └ | setDocument | External ❗️ | 🛑 | onlyBoundToken | +| └ | removeDocument | External ❗️ | 🛑 | onlyBoundToken | +| └ | batchSetDocuments | External ❗️ | 🛑 | onlyDocumentManager | +| └ | batchSetDocuments | External ❗️ | 🛑 | onlyDocumentManager | +| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyDocumentManager | +| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyDocumentManager | +| └ | getDocument | External ❗️ | |NO❗️ | +| └ | getDocument | External ❗️ | |NO❗️ | +| └ | getAllDocuments | External ❗️ | |NO❗️ | +| └ | getAllDocuments | External ❗️ | |NO❗️ | +| └ | _getDocument | Internal 🔒 | | | +| └ | _removeDocumentName | Internal 🔒 | 🛑 | | +| └ | _removeDocument | Internal 🔒 | 🛑 | | +| └ | _setDocument | Internal 🔒 | 🛑 | | +|||||| +| **DocumentEngine** | Implementation | TokenBindingModule, VersionModule, AccessControlEnumerable, ERC2771Context ||| +| └ | | Public ❗️ | 🛑 | ERC2771Context | +| └ | _authorizeDocumentManagement | Internal 🔒 | | | +| └ | hasRole | Public ❗️ | |NO❗️ | +| └ | supportsInterface | Public ❗️ | |NO❗️ | +| └ | _msgSender | Internal 🔒 | | | +| └ | _msgData | Internal 🔒 | | | +| └ | _contextSuffixLength | Internal 🔒 | | | +|||||| +| **DocumentEngineOwnable** | Implementation | TokenBindingModule, VersionModule, Ownable2Step, ERC2771Context ||| +| └ | | Public ❗️ | 🛑 | Ownable ERC2771Context | +| └ | _authorizeDocumentManagement | Internal 🔒 | | | +| └ | supportsInterface | Public ❗️ | |NO❗️ | +| └ | _msgSender | Internal 🔒 | | | +| └ | _msgData | Internal 🔒 | | | +| └ | _contextSuffixLength | Internal 🔒 | | | +|||||| +| **TokenBindingModule** | Implementation | DocumentEngineBase, ITokenBinding ||| +| └ | bindToken | External ❗️ | 🛑 |NO❗️ | +| └ | unbindToken | External ❗️ | 🛑 |NO❗️ | +| └ | _setTokenBinding | Internal 🔒 | 🛑 | | +| └ | isTokenBound | Public ❗️ | |NO❗️ | +| └ | _authorizeBoundTokenDocumentManagement | Internal 🔒 | | | +| └ | _checkTokenBound | Internal 🔒 | | | +|||||| +| **VersionModule** | Implementation | IERC8303, ERC165 ||| +| └ | version | Public ❗️ | |NO❗️ | +| └ | supportsInterface | Public ❗️ | |NO❗️ | +|||||| +| **DocumentEngineInvariant** | Implementation | ||| + +### Interfaces + +| Contract | Type | Bases | | | +|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| +| └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | +|||||| +| **IERC1643MultiDocument** | Interface | ||| +| └ | getDocument | External ❗️ | |NO❗️ | +| └ | getAllDocuments | External ❗️ | |NO❗️ | +| └ | setDocument | External ❗️ | 🛑 |NO❗️ | +| └ | removeDocument | External ❗️ | 🛑 |NO❗️ | +|||||| +| **ITokenBinding** | Interface | ||| +| └ | bindToken | External ❗️ | 🛑 |NO❗️ | +| └ | unbindToken | External ❗️ | 🛑 |NO❗️ | +| └ | isTokenBound | External ❗️ | |NO❗️ | +|||||| +| **IERC8303** | Interface | ||| +| └ | version | External ❗️ | |NO❗️ | ### Legend @@ -411,7 +483,27 @@ slither . --checklist --filter-paths "node_modules,test,forge-std,CMTAT,openzepp ### Surya -See [./doc/script](./doc/script) +Three scripts in [`doc/script`](./doc/script) regenerate the diagrams and reports for every `.sol` +under `src/`, writing into a scratch `docOut/` at the repo root. **Run them from `doc/script/` and in +this order** — the graph script creates `docOut/`, and the report script's `mkdir` has no `-p`: + +```bash +(cd doc/script && bash script_surya_graph.sh) +(cd doc/script && bash script_surya_inheritance.sh) +(cd doc/script && bash script_surya_report.sh) +``` + +Then replace the three directories under [`doc/surya`](./doc/surya) with the fresh output. Requires +Graphviz (`dot`) — the graph and inheritance scripts pipe through it. + +> **Known Surya bug — check for 0-byte PNGs.** `surya graph` parses only the file it is given, so a +> `super.()` call into a base declared elsewhere throws +> `TypeError: Cannot read properties of undefined (reading 'includes')`. Piped into `dot`, that +> surfaces as a silent **empty PNG**, not an error. Four files here call `super.()` +> (`DocumentEngine`, `DocumentEngineOwnable`, `VersionModule`, `TokenBindingModule`), so the guard in +> `surya/lib/graph.js` — `functionsPerContract[contract] && functionsPerContract[contract].includes(name)` +> — must be applied before regenerating. It lives in `node_modules` (or the `npx` cache) and is +> reverted by any reinstall. ### Foundry diff --git a/doc/surya/surya_graph/surya_graph_DocumentEngine.sol.png b/doc/surya/surya_graph/surya_graph_DocumentEngine.sol.png index ed1905f14d003bf81651acaff7bc7637f9a1542f..8ea7d1c07d65bb7c986aead71afe6221ba6fdee8 100644 GIT binary patch literal 118324 zcmb?@2|ShSzwc@;QK2L%qErZFo+V_SGfx$nm09MJDM=DCm&`(@gir~YGf$z&JeDa6 z_q+B!=l=iao^$SS?pb?(_S%+Zt@nMO=lMO~>GuXID_$lep&=m<2xKzS5~>8kHcmu_Gefzl%=e#l z?#N1HP`#*q_oNsFz1XJp2sd7rxIx(&~- zPX6Eh@te_E?;R}6%+cqs`xU$4^V!oP4Lxs<)P;}E?s&STdVogpMaxkk7iG4{m{LXp zVg5t0>(XSpT53c8UXJ|_%7ilyv1pLeaDDnxhA%%{AIooV^X1LeSp)CeM$`YdhpqK` zadFY4KH|2k>p-b<^B0dT1haiOer3b96oq_OJx|Z_VnNA;(Z-+^@+661xCj}HHO1{eCSa_$(WWF?wlzDFa_d1M!ejXbe8)@Kb{^!>Z&1}6BiG!ZMts9=6w;ye| zJ^%InOYQu%IQ*+PgW-$r&StTx~b8(Y(tV_VELrs=h_G7EIF zVq$bT|QlU&8g ziKoy1YNg+i%=Ib5O@_n=4<1yDXn04kaB(@j^WH+7D7yxdj}vGbm!`T*E@*dU>u0jP zy}iIhzGwT^aM>#=DpxHno1KZ}U%E@UekrwU19deE4whVK!DawsMyrLs6Wjeq^*a zTq*aZE*mPJJ$qK-WTQBlA>ZcXxNRrH6;dVKzON^)<)+`$KGp zs~cnaIRylw10)z!^kdq6kCoLoG+4!YZX;;X;E!D_NGsKK6T7Coe$N!W_t_OBk$6fj zLPAPPHd!@X)^W5U3YVRNcivYT#s0)8d679>#AW_jV&c$fV~mv_3p;y-$M5A|KZf=7 z^;g|D>cUyLX%r+R?nOpMYO!Jt9_O|=NM7~v8uQTiL$V;2n(;LCU`O=@(#ER*Rxk}jb&~l zd=`qH+Zi~vZq3wNEeXp%WL`0mNoG|ry*#0aui_XleCFwKym|vN~lg&IVRcW`ru~S=Em>QSsH@(2r8vF6R+L(PcJTq zpzh2y@(w6M_1jM;;{NNF{mNFt*W2E?|BXfX z|Eq%kH~;Zo4GoRgZY#6z-n~;2Sxu5*;q8Bj{Un~Ml~$-{s46HcW4t?en3A`rz&uPf z-D>jTF@8Ja_jh*$1_qK=J~c~GXMT~GxVAVkJUz`y6|`&LQEE!cu-MqQxcaUd*<%k0 z&qjX5Y;1&jvL>tAJ8ysX<3!o}uq^F-DdzCCwKWPAaq$mvg3j_qnwcF_T{$=X9+v%{ zE%jPoik#pXYfkVQ3Dxxk4c5x zT*>1px5WV!fs9jG#cta_n_;ITX!&6axPAL}f4N8B;9x@KZSnE_WMm3TN;l&l7MGMn zYsG|yp1<~S_u<2la*=F-5)4ky6A}y^913!B<~lMprwn@*aRMgHFY>lI7}3qxpR947 z=wBHx+h|Yl3ElhZ)~|hje$omG`UPg8rKN5*HnTrR>amuEg@yMYb-_@I#(s8h(|2N*a>x3IhK3sYLvK(VJt{9zRaHe=xi~jC*xMT& zFiO16b%&Bwk5E&ep$aP0IakSn^~}WJEB@Z**G$pi&Ye3`l9OvkeijY5233ALdFm7@ zbV$~Gho&JGZA)KYOn`hlaX0z;-pAhFe(p+s>z@ zhmDT&*^`ikK6>=(*0&F+xW4}W`HWgOE7qOF#l;N_;v*w9J0ehxehgLp`0-=9Y~P^! zLSwV&CUFL$bOry_eaH)l$1$_ z5=^$AE<%)zG|I&pqT?aQ3Wn2pws_y5KYd$2lR52ffZZ9>V_Cl+moJ{)T$xzk!}UH) zC??m3K!*BG_I-3zO2SuCTrOZpTk`go^zq}z@7{@p zNgv-1&$#q}ID%R-Q8gqEvuV25avq%XMpCYURrNtlbpQzj}`zd9byI^Mv87p}! zEtm4`+x+AcORi3xe{X>={C%*g9tf?dsE|IXlV>brY@DRdd?j7ov?*5m&8@423i4g@ z($W~4ry?E#Zn{3Xe8NIGI)&0#rWahs8e=ro)zz)9*xLR8T$ucO9aardLwP~_cS7{!%~7k2Yp z>37DO*U4H2mcSolk^H!~hf+vbcuM>4ijv1zYRfgtyLK+&XVK?~EnI~CZa!modS;yp z-PxNcFEKDMWOz2?ns48+sGca{?gcZj*ZRc^RtVivwI1Iw_2E(_QTa=lwC_7>`AwM!bb96r89c= z&IH0U^CyvN+DFclo(&J7%1}4vP`{}bE`Bv5MOq@ZWOJf0pHU-e~|&~bh~{GHJ8z^ZUYt!uU;d>S3p#z%i#3wxAxZjY~o z`Vk*3EA#ua`#4nR%RnAjb&@JfdU4!`>@LA5>e0YE zRctKB_-+}YhJe=mna*#H&bDit`Ly*(WSFW9$qrn6ri{isN)pK#MuV+eaLhiJOw5c< z>l88V+GS^#@xWy3#kjLslH|MNKcC`}hn z%nIv#@{GzT%j6dPq{-dQFCcQ?`PzAY{?Pn|7tIlTpPZMNS68D1ddP^!^{fPtfA&jp zes-a1dI8v63X4^5f%(txwbydrp-TIq`#dU32OOapp76K76QQ%1GgIdim*7b;{2W zf#0VGYXb$GUdG@4VhSc8xTfnuo`*p?U($93+uk ztrQ*D>2pF*&~4~r;3>zr(%aHm8d!OrI5UO*W-#0$ugwkX-h%TjqkmQxD4FEHF02BZ zlI+_@MdE92ZjMzSBXrwNOG`_brOWj4Iq}>4BV(d<#?zMzjjI*M9YS6Y+0|D0#>Un5 zMCs{$Y77=|_GC@C5^TxWb)zMoXY~lFx|TGFWL&~rzZ;kSxp6_hN0}@#C%;e1vay9H z^z>xNMRR@O6mXsQ{%#j7nn1;#QfzNNtM_x!>VxoBuER;ZX}%H+w$9F)>cy)ME58+( zw~c}~dt7tf`!}{4+NWXX=*XSR!^n93V*oiIO(dH>MWDpAxTt8w__&#&;UKO?XGPw` zG45T-kB#%{Zbc+0+40vJt1Bz>vxJ4}8LTIs;R!nurtQBkgn{OF)VkTc*gl~ZzSz1~ zSLjSkLUxqi-TJs*hArn0+xqXhy1BlLl}(D!FiM$|As=GT&*$b>k5t!FCDc_ZGdG-O zXq`%b^E5HhY(@vHnPh*ex3}NH?PljO!^)lJ281oz&*W+e-TM0O*Dn{RZ;(1r+t(*j z6DV4+^#Gm~xvkhR*xA`JBn}N-b94KR8OC1zYiN0<&${omKKRDd^Vf;f)PAgK4pR-7 z`T6tbz^)ACqRgS-#{o}aI8oQOwBK|%ZApck|c zVb?`^29~gjw#v%NLnKxB^5B+6GkiFU#>+G>lQuzDrbCB9#I+0tUbt5`(P-v&eaN4l zkzqK&TT~}%S6+THRA?=xW~0#F$>uAakhfoMJ|jE(&!4X1oN?JdOQY9WBQ-t!4yk8}Ri zTcYyt__L@JWf#k&lUuP(V6(g*u#ZmU57?NLl+?as0$gX#i~|#{H+ya5gU}TXdb(l3 z9usiT$}jBDq+g2no}>x_65#jz^9#tkF+p?_kga`c$$#H5o!o0xxTFJK zn;w>ymSqMNHyWeyX~2CUNBQkKUupxqjoKAouYN!tR8?DB3oM&cu^~YFOWyg-vu9t& z61+wiRxADY=M;C=)me08T#;z^u2Xvc=FRfO3)e-$YLAx|eBx_=@98kJ*8TE)cCOOF zZLcKs*osTTN`F&En{$Ow#Q!)QP)1G967={nbELY5MnbT{l2B@ift*QPe8BimNgC03 zol>=KhUlfML8YQQze19GrbrW<(s};0sp%Md#h(kOPoHLDViFPAz%}SAwl|fQmTvq9 zNIfR#bTaW~`RXW+)c~9td%1*!gt4(PK!xkdY+O{7j0C`;UXhJH>J|`w;5bmYBG6KO z{ld?m7ycvs+5~>|Z}DqM#n8ByXqfC}X0o&GPYmbB51MwQ%I)4BZtB~8@d8~!ohU~~ zbpwadPB94~gEFa~vT}6#az^rQ(=vTe4$3NJ+Fx>#XA=w&+4o|EoXe48-EEKK@y}2F z37?-3Pgm%8vrCsaC=Z4zBfI~#wXU7r&+(QQk*wOPmJW=GAanJ`c^GRAeaI0Ct}dsv zQ3W20M2KKskm-z5bXa zsX57b>7m7Mqo>thF{nr7 zcVq>&U5GCxd`=v%KfXz>*8c0cnsRY0^)Y>>Jf_yJw09dR=MV57C^MGa_WSeigf=;! znBQWA_${rp?OjjKs%13YFg4wruiP)_JR5uddNs7d2wZu4%4MfJC{R20QgOLd0O{Vj za|hIC71QTsRu&}%#bkbX4)HOEBOe|f2Bz%}2)Ddyec8#$cuZ!|BF=izB&jylj^Erk zsrIzO`xn|v!pv{v5ADEDDRpf&)>R+v8RJ=g=mm_)y335P4u)Il2h2$c?dj3_x=nqH zMe;c&1rhV_A)aE)qN3&Z(%0V7Zt2MrJ!bRK{zQx1cdB1zwZX#1g_(KD=W}}~%f#E` zF7>arJ2N?woxV6VDyrdadT(*3k|`J?iX|sCDUm{BZea~Hqej%8P5_N_gKoNbOF8%| zHN(%9mBWD&auKX87KLG_m9Rgey!=K%N=!^7-MNi?B16dKfv{X=m*z&QmDM9WLPK*+ z>Zzvuo&C@6r#sRdubHKjl!2=B5VZvq&9ph*(fyBS0m!n?uKlhqF75f)raFQOkL`IY z^VK?!{8F@g>vy%=8ANqk~vO;@)oZO!u$Jrx5 z*_`~7v)R`wW6*K$XLpCmUVE*=Q-1d^$7n4Cj{Q92A6TlnL951{`9rU^_j@vVvVY)u zXd$C(V47e{edqV|(-&zc*Uqp0348NKdkgC!3_X^GE>qYQb#-vfyt$V~9Ms2!Tn-#L zlCGNGZn5++kn)nacz54kjcmQue&@kpI?)YKa)3JHghP*?JaL#CDB~S;w+HXpOT`Li zj0ZgfTcq1xisrGyhc8QjRzi!ANcxAq2>kJR!T~gaDG>@Di1dM$fikykTeoif8V<=f zZH|bD7$|X+V+uQT=ny&NoVz+%RGlu5Tv4r{a)QZq;Y}m;k^7Ha_}JK(g$n(>HVlG0 zQB2g)(Gg}z5OA#f_>d;#ipg$H5B=EeCtRyZp(?jKDts?6M}`eCo0Fs^dUN@Pkf~^C zYx?o)nhx=6MrA*q@7}`}U$b}Vg_@|4iMAI1^Rw<;nP**|G%Q}3B^~J&il{v5!Gh~; z+<9iu|5)i~W7^4#WzXwz(&cxG>9|Dy~n(oDv1KfxG4Lo%;8Q` zoz_c}9q-@2$4h0mzHKF!Ir*5V#WFKzOmcqj%+|;3z4&?yx%HPf=UG@RG0TDUvS?-ww)W>|!nRBQW;=2{mof3)}fHSpo;h`%cw2|u985wkcRC1hV$llh&_b8aL=RQknPxEWVkd*UpyGW^p&4Z+M<=0v- z-+3wiLyOOiHSKJ=xXj|Ogl+qBE(8lx)mt>=Q68n`JeDDoI(RZy)G=P}?rCB7k6TZa zm3_K3dhn}n&BG_kHLl#*LfXqNNtv|@V}A0HS~@esJ)HDVl3Gl!?;(FHiI0V+ zv%TLe8?!K3Zn52AJb1_I=qDX9y%RkGE>|3#}Q)OWQ-T zjMOdf4Rwp$roVrEVLXW=hGyc2nVCDe3k9w}dC&d8m!CS*FGS=%56h~w`E*0g>EvU} zLGfnAtz!9?AlFkhL@Rav@$(lYc#n}w5rwgT7@zN>)bJxS8{3V+p=6a?#a~j~^LXEm z|D}(gIemI8YJhCcR{Z%ZEX3d|WY1_|G2#9Hkx|;7`0}L{Y&L9`3GP2-8ny90PI1?K zYT4uqShq?EB27(A*K%FHzPkgf0FV`YmLJZH@aP9$zw?7(U`P(vVP&*ZhrYvaY zTmM-jZ;!>-axfVI>eKd48H zlIVKD?bg2hw+`9a*-STx8sv`aH>v1_g!G;(!wREW@NfB2Y_D5n(>P++(-t}{ZfI!e z6rmsuM!Ib$>4Bgs=-4Ptu7A6lU{(qeii(QWH=xR1ym(Rk`RB^Y>&w$U(=r|KUT}cz zZEc)ji0f-hC}y^fj{ODZl;j=@jl9GkZ{6Cxf4^5oWlx!#Gj=Vv#c^Bg836%!HKd`T z;nAZ5sD^Z&zn=F#4m!-<*-;c7lOLQgnar4o-@@;cIZ5$cxjV;D^m-C!d3<3L%>So* zTXp3$m6hk=Y%Q$*`Bv%Qr3d4+5ES9hpFcpVG>!?IAO1B9a5!GjnT?+Q9pCQ(w>cs| zfs_YQVH(y6iC81YKtY1U_wH3{@Il~45R(f+LPG59b1^r|F`UhhPF!*j=!ZED9Aam0 zUjz%I*D^=H!c&-@{_Ei8#!73l9FUtkB$rpOUZtg_!NBWUxRs4brRm0UEcBKZCcv6xuF094d;yqs_F8IiW^fo6&E-;FI~Dcb(l9jWn|*!;m-hSj z?+r1$&6SledSA=P$Y3_Y(W^cM;K+~&rbrPA1H5Bxd3jmF*xFj(u|g-SQ(`bY;V?&7nc1gOerwn-WK6EM0yq2m`58c^h9vWQWscY5uWL#?n%uc3k#TYT zUrnz-GQVFn#BJGGaC6X;=x_}BtzwNzsO#uh=nYLwoIS8RN%btbEno1e0?|Xow4gP$ zG?3S@4xA{DDyPbdXV+I$Y{Gw962sRCxaWc7#DBePjf zQ&p7$z79no)O{v}Xyc>#+Yx}seKf#I_^sRn&H73yb3(xVpdja&UP%ca9v%r}chbw+ z+DFJ?_}~Fv9@{+Twb*hwJv|-Py|CAYknqvMYc)}bTzs3ESsxA&rQvOEYGOmG1ZN;D z!~*@j=gRbF{X*?H#g&3xg;qUzaCxI+G6<}7-Ht(RV8R*`| zOO?X|3E7BE8a3zSNn=>p@c4z@mjBF_&Ov=C(3t@sU!LtRb@_3Prv}>iffnc^9TrRY zZ%;rc6eakGp36O^s8CSrnZxg2eG0#sJoF!$KclE9aF}emx?(h!40AZR94uuwL^Plq zW~HU^IZhrVItFz{9ODipo9lC45(nw%a@r=S{rKUKlS8V}dC}57eH+A3o+&dejiWsO z?t7z0vdMHFi=pbwCe%!9Y!3q-Saw)A7M>D1o`a=o^~F1~sjV$607sswnhp!$7HS}_ z296S+7cD%0@80%8oyPL5S4@c|*YA$swS;y+y)L0rIYq^I2m+|7E<*A7`Sul_>#uJP zX5-=(CMPG)y86B82|*=3sGaf5@J z+VAX{GcGfQT_-7IKyr1zffrz%;2^P!VPT(RXE&w30ky9BW;hZ)^3hzie1ca$+{hx7eI1 z)|(w2r8<1L<{>s9?~xLZ-%gAMG$#&{^w6e8LoOnQjat6SCB9Gm)^HXT;=d4oBL3AX ze68rxy@Vy-f3@|A&)%}l8xQw3&a>?$0H_fNA$(j{6u;hxap4l&^@Me}QAOMGU9(e)SNY2hHkP1K^UmBE^s4%Cdrn2k4ZfI*; zz(y9VdVoOqy2F3*Mo;y_W0qHT_*cTeSy)~Mk-G)Y3ya+2K5E+bojaQnL=oYzoNP}g zW`nZmBO^0C`Ik>pKwzs_Y?b4+ z`Qmad6bo;BX$>Kuz^Wmb%|7qnaWp{Lq)qm}9CBPAa$exh|C2q>?NVV;=!)%~Y~bw? z5|lMA(rmYUmw|x+Xu;{~<@m1Y$2erHiFs^a2Ct0X!^_MuC}XAyQc!>6JQtspW!<*) z8Kg1$%8?^S7^NQEvb8OCn0Wj{obsQQWFD7PD6SI|6aV<4>VbG&EJ_uSP^4L2+w|A5 z1qKH%Ei4TGa6y?6aTq^99>-_<0RkbUg&jL~Oc!^7&*7}$xaDrUfl?Plb8XW8cw{-0=M<}u=^?SG9MH=YoZebX)B)$w6F651Je}D>X-Cx2jc#MvY z?%=_b2T6p5gl;s)H^!H9wG?UR-dLrWbMNn{ z09SMQv*e4emTw{w25bi2%1FQn&Fm;CE>2ELdR0F+SfM0Ad0NrfG&6&{ zr>J5Kb#-;uOuMplFH7|F^e|bht*(OBH)zcpZmwx&P0UR9W_ zrZ`KzylYh?483n}=LeRb=l;-{C>a!R=KT2bEUT~FH!_zI^Oc=|dJaLZ`Q z)6Dv@>h7fRw_ci+Ir_JRzFbXvlbQu4S62yRxU8ha^d~)i?C{uG0A?S^K{J<>zzpDS zDT;PPL^3c9Mg9tl|3AYT(<0(H=&^Y%b!hhRz$Runa|UXzU*K`#V&(i&I4@M{@tcPn z=t%Nk^ib?4ltq|BOgDljGgdL+s=>RpSe6@&yPxNk99RCZZ{mu_Vjv2h7(KQ{rZo+< z#x*$M7|4dkMza0;$LxwPKRkMZ!AU4wmFV$c_QUpZ_C|=zD`y3@uNo^a801_{#spK2P_wV z{-}8@N3U4*MGIU6078`PuV25aypSSS!6HFQMn^v$0Ro6N%c%3yO{ovf15Cfu-q@nGJ8Uq9>FGaW_# z5AbYg4<*yQ3pmP|WL19i)|2V#(J?WCP|aW`L8t=!ruMTX`e{i?NmvdbmHxtKhIK_; zTwN)M+Ts=F4pdq|GhH1xcAZ)Ek?dYSYfqC_zSJqKgEQ9B@&japR?vx+{K~nfVPS0_ z4Xl|)V01dd&ldo$*Md~HYhGk1}}zCP5yL3}3f?`cxvx?279tGlOXX}142#8T4A zSW&O?$=8TITD4vSaKc9LZ{rqO+(-OcCLFMK!lh4wLjYQ9m$?>JRCpmIrkeh8>CEH* zEJAVJ2YYRvQn+PXN+Z}_&*=wGXLbKMzVRRvSPbMy_dm&LbjJ}(DYi>|?_4DZXdWI?qSh{_kTLTzC75Zpxjc07Le(W($|)rXh2a(@^^tp-x_p zm$do(e=ddq_-IkQ?LrB7$4KHUo~6NS)%WwK9W8ClUr^^SWE74%|6o7iCP5_Oru!U~ zcM36aO;Ao3xw(_f$O&i&AM(G+ugB|Rvxf4yu(RUu~?j+ z*D7;$TyQ2v+v9gnWF?#RmlR$(1gg)IO%YU84LzAX$Mikei=?FF$mpm@t_h~PkosZXSk#>4- zWJH}};qFeQG+n*AFlKmrBpegahe|&(>=wWzVYS9&135Zb1{IzjKD;;m z?&74$e8$QO4vztj7+|x4>9#18ua(~CuF36*?L6xBSGKUxtz|-tHFEW z!Ud@Z)ZYLkCR$I0N`GWqTv!Nt_;7h?i5QFZai*c6iH?sS_}UoroF0g<(rCKzLsCi# zn@+)}{(fOzUT2Vfs6X-X@jzu9JbeG0Z_h{$?(WtCgE}74Rct>7u1Vg4$PsW1;FXTD z^1Jvb1v$CCd{ZT5Qy9pNmRh(SSBA?h09;zUu);O*0>D1MtSd`M~@B>BBG zrOAEvILZKInx>{~=zNI&DKy^2o}jd1f~jLBLCST|&48VaEjyQl`5*}cjevt$RRtKPW<|(q*NhRFRPQNa?JpsX*DE1{W-$egx;q#6rM| z)O2(NOq-4}^x~C5%$^&E{~}fdM3)=3O=Nv*csxMuw`cEOM(CWZPEOeAC7#5^!M03D z7{qjapi9j8U%QUsGmf=G z%Lx2bQ&aQN@)`%Vfa4?=Cd%+*@Wulz@O%_14tD%kFp-gg^iB2)vHaE-T!9*zpl3YXpK%e&1!wh{#&92$W z4D&?V4q~SA+U3znSYN=Aqhn*HdKWA^SsBnQk``Fr@p5=_QkJ;9({=c{xE4S7?uCUo zP~jz7+EEpB7+F;y)wM<`TKTNP96i^#X9RHPMVsH81~$38}+tw4>wmnbq!5B6aPh7Ik{jtL;`ng zy`#=dglQI?&9${r03$XwHZCrFk@doM-*=Pe4yR8)hcZwgPwi%T@t*jJkeciEGTczo zl-_=OQg)MzlQVymc{$!2u5b1DD5XX8xD+>#4jw3ZA|mD4JXT!BKI!C<^)nC?x2;*J z|F42jnZ1(}QzE#Jken^{Q6}HT&OGRPbI@84t51LJjy;&@A*R;X*9S`Hbmiyo^tlt@ zAfPWMM)0ph5d3N+H6s3AUS6Tn1;(|HK@`%}GgQ+tGxKkK6MubCQqouI#rfB!%_pEo zwv}dPrUcrw3mGimehZZc%}5!qGv2&$fpd%e%lz{42GZWaMYmyaA3k(Q;VH*^iNW!2 zMzob2+?qs#Q5-7w;zV0d>5&KLknTEnkOay{(Y31k@XJ6vp!sF%6lUDGh$@Y~*BR0r!T6hc7>%CbIVB6D1f)6tz={s3S*6{qS4J$mA^; zeX^aG%*@V^D@1dh<>XwzRhh0bg&P2;t|OkSe#8#N?1*1&Q@r|E zcaFQPw1hYyjhBF@^3j7764DHz8SDdR4PDsl*Ej#qpOvBia5R8W2AZ{}sQ@mkrgH&e zL6_R;^WI?)^QRNes#x# zWM?G@1mS9Q%0n=m|$T!y1SKM>3*ia@Bs$R0rbo|&7YJ9aEfo%x7g57KBMAt7;b z`Y3-&v3&128cB_w#R)$&bY-MKm!b{bPxJMdb_{%4|AFxkH2LZ3vgWdAKl znIv#PECe9SDO-+~V8ARppZ!?+ zZ|eW2x$eKYH3Jj)KXR+^vKB>lv7pfp1Fv;QduJ2}0|_`V9L|T|2|XI1X=-V)1)E2k z781G7z^ohkcW~@y@w?j44@N{Fq_OL*0ImJMb`m~Lb`J)pxCLZf0j@cCBN>(GbMqjvhPKS75HTxs2@o|NB=FJ0886o~x##)6IFh`|9jN z!n=jhiTKXF1okIjSifLRRBNNVNmo}lSL+K_6c!JrUTE`JOs-q=HLMi0Qed%zSMgzS z>o=ku6Di;z|eb$G3)siEPc+d0BpWr%Mau8U?2YoIumzU=}aQie5Pv`shcLENQR0+<#RZ~~T2J?}JgPIh*CeU*X z-G4~JTE2APjJAWG$ouA*fSP!xgK=aL;gD}Cs`Pp>}q?yAZaiqR^)BVN6 z-2&Kys4v@g{*`P?w%7)D4vY{jQP{$G+~$CX3B(4&YiPQ{LRC#)e0OKt^kd**r5K(p zplMWUVuUv%r3Bp%D+6vhnmGojYpCVyNf&kxS%f$D>k5k6Elsl zhh$~z?jK?)hdGAGJ$4pUuwPK?fZA9zvyo=R{QEWjA|zlO92euc_0F@==e5Rp7>?XF zCRuV7clSRC`96vrbSjgKatf83y=yqzFx}4jx3iHBv$SbVO4R^tJShLpmlw zRZq`td;+81RjNI!r8)KrISQhfoFt85g*vme$&Eevs?L*M|`gOI!Xy}=?y*)j}7TcU$ z9#no)?^h-^&|;WK_U^qLOmn5Meb8&un)MoM{lM{hn=8a>GOWMXuGk&MMaP<&hT7hFOn(|E^x>sV^yibSptD3-$il9dNFIG|Xw$^d|vz_4UD-sJOq} z%k|hycctNr5;F46rwNnmu42{z*pacRhDk6)aam(vsGx4!SDB|QE4_5C(5q_zpv-~t_M^x=Ra zV%>pSmO4xzFi3^WsIG1wx^zLV*bUxlF@>QlHH-YlqVr?0RcAU{yFfEtkP^fAa0 zb)G#+4sSp^r;C$x0TMYy3tGFFnVHc~Zf0gCDOrW0=Huf-G?}4CT}P7))&h#LNe~C= zi?)ewH}xLv%a<=RpjECD${6w=1_lOKuRhnv46h%VR-q-`8704rC}W-Rg?;|l6Wh_H zHi$A$OXuB+3Qlb3h1enC@a@A6h7zZlXdvtsI-I`F{^wB@p9w}6=MDA}lv6F{o=8d{ ztXo?GW6(2HAy%ot?`lmvpaiK#ysZ6|vFHvf!?PEUJp z*_Qa?Mc_E>MwnPx@=R0h>1~(kJPsZ>fC^5hSMDBVjaldP=a=npSa$@Qz5rU4P~Snr zykLePfWYxXQ5(B&cb-X5pejOkKSfdPE%pb8NuR{k*j%aDY;1CxXsyE1kq9L=PmG>u zAD-GqXbw#E$k{XF%stSmJGK~8J03Sfr zHlNMj@@|3DRB;P|hV8_OtJr9ZZVti~|2aQTeZlw>7-V-T1A#EPhxlCpagS3KZvL0? z5kBC*ct;38N5Z)$tvcy>8tHGC!wc`nY3Q)j^N(0OEP0}q@y)C|kVH{(PwKsZ_M?RZ zvNylH{aEr*Z0GPxpMm%HUv`_b*UV2ZEjvAki+it_bU4s~ zA=KbxYtTp6^S`4izlxF(%(!&2Uc+dCbR;Tj_*@x=BU)kcIC%JQW`{+gjy^mcC|yvu z4GfxW(Av&LLndeq9sm0EO2xgt;^+bZs(-G0<{$|=(!rN;y|q}Kpk`wWLBA=|0k*c^ zZq2)q((G#O3z9R>YRIpx&#w*Z`DCo89j_1{%i?9UK%Wqk^PTy~wFl>{kLczau(k$H zlR9nn5i>e4PaUu38y5GzDI(XAi;sf5`Q7k&p@ zl19_GHE6gm>sO!6{Hku;cO%liuAa}NI!UXr=%MT7K*gaHNBg$oV#|L0!tCQZA$8_) zmSN?}1fycooj&T!jR=zAfoTIc(9{$mn@r;Sp}QI{;m#B-Eh=XY!kR59A-17L1Yhgy z?1Z>)edGG|>!zkPijhyAP?LslKBGExC^tb)POj&>CVGI>)zMeEmxScOosi0JNa(@< zD9ru<4g}wH)nK2Cf!2A_h3^{s(QcAAm|}F1`kI}!UA|R*9{#n?52mc@tg_nXR#sNp zw64O@S}ZUT8@s4DO?axs{+hF!+p7qtJNzjD)8#Ak&*Ie(h7^VJ4<$b%*$3aPWX~^W z&)ev?M90nJImNAgTKwo;UFAelD!QEhEK?>V0ENoSA@QclZD1e5qPh8 zj91?ai|D)EZInGdHpn?};H>U8)Ye}0iSzLxx^3?6?nD%Z?H(}?+?F9_YJY(|ni|1f z)4kl1ipCy&(6ROR8oRkM=(Vs}URi1%>807ILK*swp2LrJx$@G~x93Y=X`|kU!#ls1&nf zV^B&>F@5cwsiKc!YNmCU@b$3q_pq({eXS-drCt5v&Ok9C8CNgp-0pw_s7?KnYAvaI zI4-Ezt@r5tmq8b-Ip==PrTl1Z{bi2xi?B73_@3tMUOc^-?>ul-MbDZ}q;GHOyVmXG z%CZN^S;@E1o?-B#-+73P=Fn$qUH2zTEp#iGN#`w#DPrsBgpe_TYTbz;|t5j%7?92aqa8XXOl zR{;(AdU{}>{`=`{5VAC%YA5x(3XTFu;DSD2$X_8f!?*Zg7{CyAu(!XS-xJMq;JM-> zSGR=p6J0_k!XAlB-_K5&??}FT=e*xuf5CvGetZ0Tf~Hl*))f<{Gu)fp2c#|9!zO~s zAJ|gx1%C@UA6Z_t;3G!w>Yb4RE$a>9Ntg~X0Ih#L5YMRS=*w1mp?n4+KR`Y45gkoJ zxaH&&kNKS>6SkHXN!~(|G%#=}Z=%P!aBiTZ{P+Eh`-#uG_s+!aXi{`Py(@H^_8loQ z=RKrGIY)Ay(|cCCGcnxXDZNYHCVF~cT11?0Yp|ObJs0kA5*QSOTh;u83yM$!@F@Hc zP$Mmx;N?-DLBr_iXoy$<=qM;BBZ)UGfDa*z-?yjrre2(!5x%(Ppt+9ukKP~7-OkKu zht_r06l~&!a?OWoK7?>z*t&R0OzKfGXB(wHe-kaEeY+}4_NcbmtE!2@RINyy06)zwfc5!Mr52w7Bl+BcHk}d}R(B$^kEv;L!8?)7DQy(6DC~+*`_3;IZ9^vJ(K7k@b4#QX9U#17EjdeC3 zV>q5@UV0#4B^E2y-`^jTupfQpP%8%q2jN+?Ni8`c--i2Cpf|j?y`2li1NwU1hY#Xy zQYQxXB@L`GQ*Q|}tugE@{c~?gLNjHm|LaWsLSclKWpDQWI9b_|D@`3NC7N~VBl{S3 z4Z03akhO)6osyzIU~vLBSV3w9_y2*Yr;8i%U?_KQU#wlD!j;#9>jKyh+680>u)@p9 z*;b$u&~&$5ckq(zBPshtCE8fF7ZD0(@n7o~9Ic%s6&%Y*b|?#VxVrkSrVO{AfPZY!o4B$ad>LLjxbQ2jm%xm*VW%y789vgA zO=dOUO7QT?sT~lo>;JM=yw~FJvx%Oy1MUo)k2jKUt>4YbyeWsOgJw*IPJ%UF^+ zXBMEGa8GH7jz_>{y9zv1z>t* z>FMTXX5;>xmkbhytvhJ#s290bx>x?Z{DV23N}{x`zDD`j#^Z2AY+yyy|BBTYc079pP#P-KF{f&@9;ZRARYz3o1TOOD7K5=3CrA~XJ?b87*p_^KN zYO;D}u5CzOzViZ~khaA#cd7P6Smv9|<)_myQgp{=ElMe%ig2+)?`{_ zdyHOR^>%odn|K{J!MQ9+-%IQ-2CYo+Sni29r>ks5Kv^gT+{b4fJ9Z3RPaPgd5EMXL zHFR$xjWIN=_$9iwrw}R zyg{s%cvFSKF-O33Bq;|HQe15h^2MJOQj^Kjv(D9#dSu>v6xI0zRSn#Kzuv%y--dkh|@4 zz;_9(nM-n)F7K6E5pyE(9m4zr?uT&WYl-4cyr~uzJ_sPXDrZpRAXT{HBH$)llT%Y2 zF{RQc1#kxkb{TurJ1EKcy2-*+#+|Y|kFy9YKmKvyrJ+{y_W&`oh(lB?O_K>DkBj4a z_*q#W)C|u+=@NcqQy1O}sSzP7E!K$ga!)uB5N?pcJS>t>fsQpp+_)xK1}jifO3Ep} zZ1bJ*1*B?;H2|xBMgC!cO=7dCcv*suopqY6nV7=6d!u{$<1{@y*0H`s))(8L3_)rj zGI4^kfk8P^Mz~E*4V{-e@()M%`75Sk5)$w3gcGwcGgHnXLu!hZg=OlL`OY0XS|%1j zPT95dz9TJlLA$8*qt!>AK@Cqg#Z5gP#?sOSae6LA0nT;Pu!L07sJzd1-%OfDm{k4s zEty$ZP_EHhyWqKf$m+`3Beb;kd`I8Nzi1IbmlsIURsu1LfL1Tw6hFGYZ4ip!!O;o3 zB4)d+m9o3gr(L(m{^I6V{fmYw`H8>tfYq_Gft)rWMS>GU7;~BZ!VmG2uT%-_7}Fc` zK%Eq z2|yROw?J<>#u0(F^sFpm52}L$7r|({I~S1&KuP=xk_*Z&)VQOfo{X)8gRy_f8*%E(X1DeBmo`HIt|r!|#DJ+;aUjVn4Xm zRShUOS`UAxxIw_E#!;xY35KRha`2OD;I50^ z7*{l-B0Wnd+VtsDH-3dI?BwJmB=`t+!zLpR@k^J^ojYf{4S;^^>sRm12f@KaWfyz* z=6DOEre+tMNQCQO%OV%yWIO{(-^DC=M+{;NMIeR~!zO|VdblhZDd`{NkxH8nT_$Yb z6xei|cH5fxOtAGbz3U0;ci*@3?|h|xV=`ikZgt;7!>hSNubXD5K8VJr;sX@pg`(tV zpHq9U%nbtDXQ8&?I1$rCxZS;*(Hh#7wPhe2XC7{D1Wd4+!A~KnQ4*ddE?Z)Jxas-% zk5qCXqUP={cKMKI-*p7A?65OJa6_P5POcub;nKx)HqZxNls=?I(#|VL?8iD;QI*R+ zml`3s5LbG39l9YgFO4*s7s4cvtoy2o*5Kp%5p>k6GZ(&)fV2t6V;mmid`A%gQT|Q|cyv3iyvJbT2 zFyUQyw+nhlF;ZP^ZF`~V+K(xk?p-@)J0w`UJ1>%y>jK@6_jkxSb|@G853=4nn(P06 zAAg$JBwNGEPNnS3L{=0@A(SL5nI&aqlZ4C=*+~f5BxRH>n-s}jX-N59&)55WzW@9l z=XH8Hr|@{*$8BEMbz{2l&_t`zrtyp8yI?x1%6E0~F=>~t4!9n%RLJv0DdxNt=6Om)zxU0QOow6wIcnp{|*8(SB(Q)c~riR10G3rv)>(`asHCRrel z!TB3y+Nu)q!GVE)QI3QI+RY#&AVA)9sEqL-C@$@-t*5Uig`|=Psx4ofYuzCDE`|HE zGBaPYxBoM8y&ik|0bLBJ6BJ&0f$v#@g4tH1 zt+ZrfghnPO7Z;4}Z^(EM#Z2ouT0Vfmdo8|yEPs2?r^@D!TzOC#h)x1a> z`Wiv-^Vy4J=Xs7Oy9y>~5$^3h6mcpC#HB}%*i0rlI5QRY$6hlx9|IHYlXFLUdOG~!=le4Ktnn^W-Me@_8&O%xP-6_A%FLP)Qwy70t+iE8m& z5{jAxLa|9i(P2qR$Z<+ZK|zl-vk5NfCy~=~>=uz3b?A^Ygcuw>mxO!E8x9W3-1j@{ z=vX~u#o+<-1aut&<+gY4h!+6p^v5sf#+Y_BbC8&tmEO#~Xq+gakCk3I-PopP|+d z3C;UqNX}oZtgpLzdM+nA$;?K$5gHI<3my2H<`ryU`~g!BFe?FHAS0lgOF}?k=3kiGMn&!qr%Z=7{otUi zv^3j3xwFu5omtP(OQs||I4M9N&^3J$e1$k3eOoNYwWGu6lp&!MKX3p)n&jW#=gm3( z_x_0ed>^<;-AS9>iTg&+{nqjj5X3kKX6MccF;PJy6E*6OqI&%w%iER^hVeyz!=Lqk zeZOT*2@7pPNWoOto1E^73Qv>8~ z_2qXAH)H3*XtSxe5y2%iU)qC0Jj^eOUY#?_h)L0luoL4V01=9!^0#iq!`s3c$;hfk z&Kp#J+W%$RadAZP5x<`b7!cI-^pB5TVM){lMn=Xwqs^_~t`N}7(PyOzL}f=y%TJkvfSrm8 z34!8}w-Rc#DieF@%o5__+|faT`pv5xmwAFiSwvVEC>*HQ8mxbKtnd@zDBikAX)%CyUu#|>3mH_ebp0J{l|f2x>L-QXW#*w!pRe23MlU@R;Rb^a zNao~}6f;xPcFRLJzK98*Y*liyvwNbDh8hHlBiFAlBYS9Ul|nONk^wOxYTLhF61#y% zxeD|0W|3f8OWs0e3{$zEi7GNO62Lz&pU1Js0NYId+syzp-c=MU34~Df5~$@M-~d*V zm|;UVquys*X}1gdx(K=0_wF4>2?Q|-!kgjYVNybD@AtK}xt3x4po$OAgLg6qTA18@ z5_ruqcl3dN=fQN=sqS?eTME`#AUXDAnre#|3K)N6w$~}nDja0{J;QW|Yk>DTY!S96 zH8ORxMCM+H83ZwP1p)aM4^^LYYC;JZ>0G<&~O;UY?K?2k1rRfbt z?_W&IyjQKQ$#0J#OjDakj*LVP-yR+6zO$0_^z;e?C6|X1UrmK)%cblK2?veUxk8-2W)OL4$mQR7+{=SFe8pcWol+#kv^xKQl zjV%e6+lM^(s-s^=A3t$?@x+ho-rKxPwKNim_5$+`Gn@YY{%BqSPn&Ccx%fCh6U{U0 zkaVE%0aX;eMPw61yQmn_+s_JXh_7$_Ub`~b&(W=eyB>c=HIHgHOUrAM`q-^YqEh`Y z<)}7#Pugw0T}X!ODLPT_Ve^0{?M-L))vJ<{7LpfjBqiJPrU}0P4*U%c4H}|eTu}G$ zxTNr?#{DK8d4~S3ojZ$Bh6fBCEEsnS2?RYvWD0yhi2-gaBs~~tXy)p?Z!5zM$%BOh zIwtz!S=UUNwi&TV2U%moq}c35g;}fHJ$|aZH&oYusMmSXzK)IGa)%DXaG1jcZy@Cv zve+93IS=A>Ttv)Bc}z}xb|7d=7#_5JHtM2|=XB=YV*sh5?VNs;-E>TLvD zgj2tGxXO2M37dgZK+_nJ0#O#!Q|Bpy_I~g3HfSyV+T2VrBKf|HDlI_NmP zlFA`E1&DqC6~YCo*79=EHCLuZ-#*iQJZBC{_Q}Q`xLQFXJctoqRlp|AkeDr+&-rEq+5}U33=t`}6N%+Wa5sq0tXHH%Ys{_SL_jPxM zEFFJh0Fvk@ARJKuqNAohy@!sn`vejw$cY02R1VBL5-Pjl-Rgg^Ix@NY2lgIPVk+cT z3{tjn->};e-IN@q`du*0_Jk&V)Z4kY_rFAqm{i_Kx3=t@65~DDlQ=7KK_u@&-t2of zyg|<;$w$f_X6v%-ezbE!!QYOLA`z-yu7gs*Xqi3xZu9#F3P_T+0Y^#D{R%E{#5-Q~ z?`p_4b!sf@ezpF}akJUvXiR993V*lu&JMM}%JOY2JA&LH-y56@MWAyY+Wb_IqX2kEt6P^D;fo0WU>BUWlQKYewZ<(AVcm^?vW=AXf6&LdM6gx6ioLjL(Zq z(tm;EPa{Pwt-guA60?tyR-S{P6d6I*r9<;`)U)P~(wDfoizCb4B!wQnJzk@6=CY)i zoR*kK^y8K+XT1x$Z>6GK9X$e7!@S1#l4rySzZ;tw{68!JOBizA1t7QiqQyb#?CfNe zdg`%rZ>WQKm7b`uSSq z8S8xm18GS7al!Hvtf15_eT*t zVi{~A^OY`km88NZE{`hvcC)(%Gzaucb)MAhduac~?X%O|D7riyk4mwYsUKxOocQGL zB&2@uX=UYa^tTWvoIQIM@dT2L^6F{@yHk$wNI`*=fDZyj2v}^QCc(RR!P+O%uHJz$ z9hX_Ny;b4vG;wvJ^OE)9!`T5{jVC^Re(zb%I`kuA`|ELEov;52H#{F2_ZzNL~^%=%WjY}bgpq1aWaEcsIkOQ>knC;D)=&}B=X`Wx|+ zlk2nZw?IpdAFv%zc0y_JL94ZpP!4K~;3D(HGZWG)E9FE)W&!I%Nt`1HrADwF~K>X1XQkoD#v zRo|?T*po-<^BV7*FTt%esW;iI*m(E+3cbI(SyBDFyq%pL!gtg<;2BkOdWBA<$bvrx zBml}DddVMqW8>mr4#p~7g;0JWd_rFlwW-NTzqc`UJ9svJ{0rFm!oka<#CXl{V=q>Q zHiT4OW1r|=d0pA}7p<=!EYZASG(w?qu*KRY`$*3b&O~qKwCwbu(1o`v?_{Y{cHd*D zeIk>P!c+9r=%4=k?seL0fgBAl4;`QlrC3TaJa_Cioh5ANIk|IR%DzmKS88UyLly=* zR-GzI`o!avnG?Y2XjoskkYpyY4cs4!<#;qAxlZKNSKP4Y;^v0H-zE|e5Z`)w&QJz` zHS!tnkIIfssg&a$HAj4i#?u-HbS`IKIb4E^ofz?+O^ufJa-z=r?EyNWk|VKqoS*#t zrGA&XP$25j4k-gR{(>MgGJ@}E8hBj>R{FACL)|R3wMX;{cC}hte~g|d5QbeGugr`Z ztad#ozqpHuLdM{& z^0KbHEMd0O5-EFm@4}fAd87nes|&`=Vnk~_IIMB$h|$s51Iz0c1CnAe83Xq=tBJ#w-^#KLg+c!r0o;$_6wZq^`%1Qr+%z8D=fi$3`@D@)2@@DSjQ zJ9Y$vUwP-n&I`)BA9x+#QhsIaq@<`E_2`_oSRivw${7iMh#x!1jl60e#D4}hwtbhZ z1&&DC9VY%4%jq(eaxIv zI8svm0|U!e?2n;yfqD;eB&17XVga-$kR990NB|dBY4aPum{PcCk4<{LManD4Zh&!;IIYK0}nco*RB{!EMJAzOCyn*_Ir=llcRar1L+?qyeo4W#gYIm!EVy7d-1%6**k zdkl86UCOl;RywbxX~hfpK|S}@-hy5BJV#87s#~IFf?rGXYzlJ(!R4Jr9sJxmF`WXu z4+QhSezj3bi?T8~SAs$f#y>P)0*n=p>NTouEC?N5ziTC6J|yBM|H)cjFVb(PcI|3r z_9ypR5Fih|C*0_1D<~~SwOw77?!zEq$il+Wa+bC?!|JHu+rHN?z0MwA3?{_C|8U7# zRdJy81yfvXTQ6l!=JyP@Dfw_awhxmZX4_<`(PHFd4s~Wv(iPlA6X;xv&Ybr4Z|AJu z*2}sg2YnF?GHCU@c%t1{>JwaiJ7F#2{F`4FT6FE4wuoX zRfWycP&-iVa3mzs# zzZ9N>PubYIXj6}*O?1~8s^2NV0pSG`)xEbTZT2r89x~JxK`6XkkW^$%etzTylK@5Q z(`NPaolo8xlasFc{r9=L1P~zC{QG@b_SE+`UqdI(dJhz3SyWe-(a^3wTrGKIMmu~j@CEBg#x1S? z_tF^^D=ELd3uZrxYWJ5UT_KSod3GB4FJ|K}~@gM<0U*3CG}=m-suN(=XYylE1`o55SGeAw7BXkDI{I9OBUHi6W~sV!^l1yfsOp|f*A+#4FHa^k-w{P%Cq@e9z@ANebx zs$g}efAip6-8aA8SIE!rEIWC=GtRJ`Ri#^0{0&l)@77+5mW+6}m;XCOBc}(5b%hbwQ52YZrEbUtL-FDwol^o{esO}!Ju{+_)+-1xk2YKKH;?_dQg!Mig_f)7&0o)~XP(eZK5WFES$~y zrK8fAxT(R!0~_K=_-_l}wd73ipre>mw)c4>HDV0uYSYjvPJTQv8&?g3i^gD_*7^Qx2t z@m$jvFKtA0I_7@3;;M~dSNBOkSOn@8C{e3aZ)4!qXs`;;r$1r`8}?Y*tq4ySF8*^E z2)bVmAR463QvA&%uKyGYvRct-W{-{WuSVzjRY||ktFlEO^<`_oW!h8P+1C!5=sc5h z)X`5L9Zb@1@64RhqCPYIg2|vWQCn{$D3If-!t>DY`hmK}4`w<3whtd z9p=>Dh%NjCUvz@5;4$)T#u#*bfC0$9M{}F0b{8>kME0i6w{!c%t;+sR<76XS!4NZv zbC0Cb6-mg4cj+3iPR@RsA30H`WayYv;_>EZFzu^YhXRq+%uig?+k39u8=>9?qjJF_ zHhyG`JpA;Dwh#$E{uX~q3hEGl<@N1*7qb|{7@E{t&VTLM4h-ZF-%CeGkhQ*4&=l%B zXff>LxG@Y&n3R`Zm(LInUzIBY&eU5RKBYzY_IZen972>vCK#_cbr62r=Oz7ZiiCZ z1m~SR@!gZgN@jzim@nuInD40^!~h5YF&T!PjI&iRf9a4w$;%7voIxkigFR_GuydvYy$e0{^NI}=cu#A|S+}YZ zFWdR!8E5jG_dh;$&O@6wJt*M5#+BzH*E*MgUHjdt*c^E8acgj7L(2- zb;EHMKMZ0fH+BDJ`5Np)X9Il}frr`Ig2EfWyp<&0O_H+A|7tTHSBHpuK|%YZvzxp7 z#`o`lWqi^HT8bfryG)1hZsStkTz%KmIO^?7hKuw9TaVq&Rh-?}fTV;^6bZ3>`>!kOz#8U80< zpNvU6(;z89?xB=0v)8z{ zy?pdWdbn3hf80Ld(?9u@jX*_c_>;TWVA(+LXA+lT#OlxeZow-0q0gU7EGP!4*`Y5r(aJ4dXLB!7~bLG3noua=k?vi%MJyD6H5!zkU0LCJ*sJi|;RS?1aq$A5$gu4GnPpb_h3acPaD6 zx397%7>S+`rC;59oG%)_TA8+Mmh^AiuTsM7u7GI94FxyV<=2HNrVf?)B4wL==;x!Q zXpqsk4PfAlgIwXwuPxM2+(H31Sc$Tx*Hf{wF5--hNU^n*t=GjjJ8056gBfPS!f2+8 z_JpdF5@6`sygC3n#u6fv={5Fj+3{q&#ai<5soizMr=}+Fc^ApMm#TS88r>=zr+~Xqc)^g_D;Wweo5j3eEV<3z%fwfPIH(pJy zItQ%$8d$xP`!m@{^bVkl!1W=QCbl!y)+|5dp{oqC#+54++otEwU$f5)Iz^r_0&H5K zJ|9D$Yfw10qK$&^*rZW37UvQ6cWQonuyEx?A^j_(_ExFADkAT zS%NWIncWBnBCK{mN3{0ME?8WId2Dzb0-BiU1*Y9X{N+u?rt~~8{ z`HtNKoD$&L;Z2aUE-W}aF*upo9b0f03)DvlQ~6E}wgf<~U+W=W-!|<6hzj%t0n>|w ziXNJ;WAY=;6Nc9z+u!k#{&;$WG55*po#zm_>67wXeYx+92!`zZYuPx(b-g&f&eY3_ zLN$lHV=avst#e32)z=`hj;R}XuG-j;5NdU))GFfUjt8oG#!6*b^YfLjD; z;33+;hK5ZxrEn^b0VFmz|MT;@GV|C-PeDJopNeJa5DXAv5fvV)KsA#MNIH6++U2KW zOD-BmquhXn1#Civ#}*&X!C@iW+1cmDzu_UDnXz-8w_TKoMXg`|Jr5r;laU2rDg?*} zV4cI*eovrB>T)hRK7L2-G9=U>jrH-P2Pou#K7xK{u2mkNFN0t5r5Fpv!Bv@;M_oCD z)ROQ`d!W19s&t4ZkTmGcPIPlW1nN&!)8C-Yqf2Z~&Ki#$0s8{gc@N;i(91?`3D5$x z$U)fZ3q^wVsl~bkM-pQwgmlNHvV+^#kuI}z2EiPUK5de6A8zp;pXXkIG zJ%x}&#w6rK3|0blb7b$xowE<-V*5?P)r?rw&b95p^#qX-lYogMean?^(?wdSOC}zJ z=qHi*gT^{E=H^j9gU;*x(o!8780hypb=LlQm~9}8sv?{p##Uhq(a!(Nebj69^9iT` z09=puDB3Yn+C84P&pw0bGr*m4J4NGQ>lFav0Q7~33v~!&Frzj~mU;pNUc~HMu{`wEG9L zh5k=v9p2_BAfW(MCXlO(TM1-5n6HkGj%bT6fs34Vrav|O!eHH<>a?^pEPdD=BG((W z>*sV^UYA)}I5=QxY2t|(s~@j^ ztZ(akuRb;&4Hu_BrSHD^g%4_lsAq%|AKJ9esIV}3yZ|5>fL0U(Pk@iR)b=vDXWALi zB569xOw5>p3T<)m>fad5$NPo?u=8)AZy*|Cpi_dKp{Aw|XRp_>d3=YTY~k)W z(Zu0+)t=V@U~-UF!1QCBT!6dn4wnR~8NbRw z1_8(rsGl#usuShNv0p%vjp%*OG3`r+BL?*X#ts$K>mkqj^%h1e4Tltlb-qp>m6k@L zfLYgVZDFT!N=?L}A&hQ3G_kWYGA>~Ukg_F}uNIz`u;YuCwQgZTeH9#M1U~TV#tO>b zzfe16Cs;^O+ivKxP>`9de9^ zIOVIVDq|xfT#(#D)N7!e1GWG^Kiu294g^nrc*e_@-@yroTZGCz0C3MB1P29BNW4)I zWPSeCfX0Ps=(e`GC5IgkKv`zE-lx8xz+O-9gAZu%5SY|WJ@y;;JA!Zd3Hn{Xfde0L zuLqX$GlpC*yx%w*eN8i1g_)TdUHLClQ{N$BD<;EQcH1sVH0`1qjv^}gNNwBu%5X3^ z-$#gQ$6atJc=n8rh6ZUbFqf&&LCxlya@#9gcm{fQiMmA5sF9_WpT@K2MgI>AFyV91 z?3FVJ1W?lcCHA{;NhMT0!;B*Dt_FdCI}AQ6>|N79gNPA;iz`w9FA(9d0gVQyIA40s z=RZGDeTGU@x~wqxh@_;-4ps+DJB5-gT82PJVYoeM^|!KW7hsgzJo{ktEg(E;KOlkl z()-GYxV)ss33qye2}l8E5{wKCILA(=JP$k!ag=I`Q_l7}dBC@?G#7hq%po4w%0h5P zNoW{Y7^x`ATwDIe_d^W~fO8gih+@XLFCfHeFG`gr*^8l!l#_Gr!iC|P-N$fJ9qsHu z#0ZOsfYKkZ144vPuotjBzmy<0?&|F9+YX~2IKGS%0vOhz#BFuUV}RAbyh4gYS63HQ zX37owGiQhz_`u&1%?b8p*DkWScYyX2VH0pt2vk5!I>HeFFKQ2SiwhR81Ag)SV+9l& zSmnn-@u4RSLPYEN^Ci$ZB+Onhv9UdR@WArgwaUAHy{_EqmN;Vboi2?CLt(j17Zop zF#rSf`%_{68_;2hjv_5b1SfZ;Ef$PIXs$s?0`PNBqdrK27MGM%d2Qscd0M%+lpvps zjMTgOyeLdpL7tr8jQfgh+x+tfia*yOlYpiv7{myJpz!ASv+w9xrP>|QC!zfP##*xa z-;-mHl#OY7s|xO%I}n7>AhOjuRrW?k(R%_>(h>X_M9rAfEr1;GY`cb+*CxCIRQ=ts zW<+fS7<=FOi8+RvJAcYuSf6fklKf{2UcdefQ1mNK#HxU)e**hsB`+PFc6`bq(JL#D z%52$bRhzTRH(sdle9QI6pjo1}1ktfuy(TwXE3ZCt>t6_rE`q543HI!?7;<7;Xul>M&FtGrW=koxrFJ0L+>voj;kj zj*cD!Bow?i*y5$!t+Qqm79gh{&t)*tN3_irRW2)g-ws^(K7W&JkC{b(V)<<<0*|~rfKZ(S1M!WP@8NZ!Lg z{ntRFgku3I!a@2rl;}YnaQleK=rP}m#LRAQe&5rOD^YfTcHTjM2 z*I{KSMH!0}q&prM-cU0r&T~|xyPf><4&8p{R-7aBKY{5c4h%eK!9cL}%#uW81)UEL z+)1B*>-)T~XzF{JUPkL(KL2@~E0O$EQQ-;9G5l?T9Q#?LUwh2`WgeB%7+L2483d+W zrP)73Cuho>g{#azGvf* zr?_taYE}SbTK_^z5bLULtqUsMI2NdOdVDeRPU5Pp?V*);b-vcNHXv?;qDY8ZCj?(i zHz5!fo=7==Qt`I0-a`e=S7+xJ2=0M2B@q4}&!VgInERX&p@Cadph4dR9f5?{|Hlm( zz%C<#afS)Z4=3F;TG{hy$L?`E!ax(>St~hRa(y<2sI!Uz^(b{DCJw>sp)N%rpt;OK za|JV39Dgo++stoynR)!18=%hbcF1FXyY-bT?qB=7>b$*J(O%Zh@~Cz1sclt^OKjs` zuS)0uBM+;N!&jYfD(VJ?zTZ>^Ou^9+;Qzb-wr90 zQDrb&*tdNeT)WPOhV-$xn{A|imcZzuJbczuhE?B7?6Oh4sxuY&Yw#Z7{Geiv70#^5 zixCmdtV%hXDX##$1tK{hfxWLHafZJkA)t4Wnz}bmdokD9$l}d-M0hwQ#ot*IAA3x>?^!cKJXLp-;a97ITY7jV2=N*SS;66URWA#4l)+hbfSVq6#@x;w% zIB3{K!~!x7gP3W!nzp@#J``9f%Fs4_@`M+oM-g-_TWXkAouSHa&pE63?DJtFIfgj@ z57KKZD=Q5R4PgH^`cXNad;J$V>>n&V@s)8#V$Q#m)Y#xw=afwJ#^?9-^#uy{!uj)) zqB-j#D&r;{`L`~6z!V@8!Q3ZOzXqgj?d_lnnj&D()71r_oOBO6!|vfx1x?XhN;aE< z+v(xCzwdMn4(1jVjLeXy7ziVB;tg*GW4>*? z4Q`fXn=4?~dP5!Q;~mnby2;Xdfm)8YcNfHamYPw(=O2=m20#r;E;ubomR)i zPB$$+qQ7Zcs|uJcOft+PLY_!Q)jK1eUEJ-J;gpy{X3iIV3|EYQKSj zfiI??X08n^=>6+Jfnm42TT5%JR&ABxhq=PKx{IQ5n6U9ccj@gd-+p2h`PQw4k7+(ceCQjF?I_eO=z5jt{ure0=C+)S{ ziWF*ID4t~2*DH_iBZ_(;Rfhfts7z2dR7%@k7&;<*Du@3rq)EY!K-u9A>KCHAsI+ke zG=@Y*0GHkCLIhI#`;>oZ}f@QU4nW)_rVHRz08jVE%LL5xq zr6%uH-JUD?pC7=O<3}QA$bR~C6l$n&-Cex-pZkuQDAl61W$EN}N`M(@QUAkFvDqS8 z&z=gNiGRtnVC02L4^HSsQ#U5$>R;b;dfOR?0K*v-rH?$|I}HF#OrdXp>wUTAq$Z;= zv)oXwQ1EKi8Pboime{M6yC)HRVC|r6`0m;HZP(~>ZBDl;wnZ=AT!}Q}4QI`$C+d6{ zW-J@2VG1Izu5LtD)->+SusJo6oX1E{kCAW!O0KdUh)SKVULEL;8E@n0zy6^7+x+}K z<+~tHM-rca%HeMU5aC5WhzNC2OI zqjD5>6_SM!+%g?aO~(b874K}a5YiVKgZAmZID^hJ%t2{!@z4t^vQad{5uoa1b{ju4 z;f>R7di{EIYz!hT&=flcfG0ua>{-^Rci{UXtV6j`;vhhC#x&o;P-;9vnpa_SrGxuB=P0B`dj@G| z!CD^Xh{(v`bKmGm?mHGrpkf?kga<~F3-9KNqzf5WenlMjDRALo7PJJ;6trL#NMm*} z!gIVN;x!*Iey|P1=|8C5VZH6?O^S-b-mJ8?#gYB-$6<8ioR=anApJq>6y8A`?nZe9u68*I3no8D@Jv16(2 zpEJI}FHOytM=A~;JGOht($VoRD6b+jF389cru@Y@7d>O^PJJI`sl;SQY~8OcaF~&* zax3+GY-ynq6=fxg{+$%KfVqw1ytpA&sG=2C9e#OWH?jV;$9%#woPI(NLp4)?>qc<| zB9lYN@~_Hk*E+^-I-S0bY_u0_Oth|!@B2YbAFNUnjp!AU+ckoZ!1BK@emgNoPf+2DMmmiLs3j<1t=??= zV!(vf|LN1+>=xe92?@DdDxxf5NbM@-MAod-o*2O4%I;>ssL<7$fCP`R98dbc=FjZs z7Q-umy>^08xN|2jb0~b}9dB>QcSQ36L3v$WG>)jGkJ>uMB0)9)DlABe{me5Iw3dN- z;ET@9$=NY;1IpcK-y_($W1A{UPCA1W8i5~<1@XaDEacB3B2cqRR`7gYMfq{#1j_#C zTB0%@-u$rDnmA+c`Sa?uRm*kDy`i4@1|R;KBN2meGk!{3baZ$9_I9!7#HwE3_Pz#X z&!JMSq@`Nb3&MeCvKLOP?_{#pKkrWyCd{q+`%ZlbkD=<20ZH^(6=6da=>uJo+E=T3 z1h4fhn7tiaj*9UXR3;8`qH)(!j-QESMkTU?l1Hmn`Vb=<*@evXtB`jBtOg*DgPLH7^v+X>NLb$Qh(J% zh?(OmwRs7X*%43+eFa4Zq-Idfhf@OAK(rDRbd(^A8 z?rxQHDr@Rv&SOUoD13NloSZ2vhX@f+h#Fy~g~Raem;W>PIPtCZpLhNU@HUeUnA;|EMWe3yF1%R#8| zz+a*)2nA7vs}#OGxmLaN8`hdBxRiKbcuf$BJ;9Dg%7h4BYNDR|X~p)QKj$sTpe^el zo&3QjVTz~a1P|3~n^Ne|M!j>*i+O&wt^j<2YeU!EGL$G2`s~Wk?c?dJydudN-i+ix zUsu-wHEMKAyEp2w%y zsmQ&R(%yZ+7$AQh^o3@N<*QaX{nKZjblZ-;!}SQuU=Xmv7H{^{+pH5E-#Uifjg+8N3gI3e*)AE+dF49Mkup@}KiNmD8TDk!O@4%*Dp` z5snM%1LxK^rj=BkT``xSo4sxy(0qxPO6|V9y>#*cBGB4TaW7BqM`&dd*P~Pp;y_Kj zMbrbBqS=YZP9XVo{*_|%T1=$d8Zf<})@C7I5`8!`- zsQk@HRGCoWLOg{>LE#T>A(n?+6{S6e&Nt&;^M#w7WF@>$E9MMX&{MO12*tdqqgOqu z$Ij3PSxG2$)00A|;75CHSuNBw9)q{6-Z?5jE^ie;bk^6jl z9~!jCT7biVyGEmz_iD_pP<2G(J9q9xc?WbD{JEknM&4hFv<~bGKuP|`!PwgJ1}=fH zg2*B)%tcTdHLuO0!Y5f3LbAy_QkckjsYnan+Lc}6puC^b1K^V^_ejQP6PGa3@$?nDiPc0LrzUlVNw?ec}5=h_r=}6^nbsUts3hb|D}J! z_@;-(N1@#q9NiE`?2#FO-01Xwgjm zK*>GFNu1))RZQXkRWst0c?0T|iiFBpUS)YN* zufN`Es7EjjotVEepC>UzKjcu3Jwn|852#G|6FUVMz*?JBcr!Y5G<3uV)c`bx80hF? zwX<-cUtA~eJ84pA3{<9pFpw3$Com0CG_DP?azG;ntn|(zwVm>bNl)il(^6NzS4}tH zJJ>sNdIWPoglDyPaaf$@oa@I{UF?#2R#Qqi= z8-JdZ`q~anx~7ZX?H5Q2J|(;>zMOG+|NDm~t_y?@zIDTO-oe%7t6LwoZ)<;xHUC|G zA>o37xlUp6$Ra}d=H_OXN_-qhG{oqG^U>n;i^?Blf1-%3g#~hc{0ZVeVzRa38~1tW zD-Zhf!0cQoG!|w7G6^00j12#+$M}1pksgdKXCtZ+{x{8TV7VBd4c}goI>3-!F`#mH zk5cp;ig=M4{4%g=lg_eDqr0MM(`qxe{-P6tz^?Cn$0|L9y zmVR8%D9)zC7k%^g?U*QZh%f?AG!F5f*X`S|DxTM3e7WdiTAxCMS{oO2dRG)aNJ&Z0 ztqzb`{^#RUgoLawt*@>E8`M`9E>GHPdO(Hgo<`=`ZAy?XtZNEV7aP9VFjkK(Im@QV zMR_xrF7prwVHs1)jEjfY;`29MuPWW%beIg?6;xHw>&w+a)0rK1rf97w$dZ?=fbwqo zKb3FJId7A9dvtsXX{2puW9XxMTE!-6#gT0!+Dfa7x!W~WPkU!ugm7qnhrYQ~d~X|* zzD`IF%VE0PtJm~n55k)cMWi?fs{dP>&FA$IDu~*pHO}~HYH{%Z;E!s&zY(2OxgQ_q z_MI*4cp^L(;Wky_vLQ5ed1G6;Ff zoKwk-pwP&5)Z);9lN;bvijgS077+No-%VvYwLn{lf^v39o?rHegBQ1q_o`E9+N z-sqTTMi!b>Hak?_Onjc4l%Cg^M+8EGOXc_VI5YNWFXU@sX6XjMfC-s~WB@le=F(79vK2?5y?!qWH=R!VzeNNgxd7)s- z-1`na&`^bE-2FQ-<#Ob1stY_!FHFQ4g85l!&Wp1pe_*2`hg14sgYpt(0LdS#KAM0= z-!HFF8VXkLpWm2zph8@YqYn)^qxIk0lwK?n3Z}=90f5hn29yui@03%Jn+<)@F|*#P z`xPHO`lLGF>Aw+;*5_!4G2YCJ8}DZvHf|{K zR`^^h6)L}Vk-pPZ$0nP7>#Wgs@uq_Na~Gu>OKp{${ybCrug}5KV^&&gNijKd|NY+Tv`g0j6nLPzuBc=n%8eJFvY0o`%?R^&*nJknhqpo;w?gAI> z)dJlOhpWXVh{lc_e(*&(MwO)L&|2%d2l8+PCXpJ5vGtWRgxhA;canpLP314EaP`5F z6;eXJriu$U+63g7a_nuj(WF>AMLqQ-t>V${!>hOKZ|oMjt*+!6I^-be`f$uiCQ)}v zEwlUN-M7OI(xpSXb@#UB`7ogCE+$Nnt3HUbcDikIc*q&?8u z4D}XdqO#;E*m0;<%DbOCGGDW5E@~=gkXDWEm*>)rY6ZGfhWO6#!j6W`_im<&SyG>N z8z28E_p?H~B9%FHt@uxbapXUhbrM?7G4An!&1Yj0V|9u?w~x3U$+!2rc+cS%uW3Mw z+)zs92#(}hbc$d!oy=4wygyf@HD!M4y_5;PDf_(Jrc#g%YCZkD<)yZSz;U|`P056Z zQ8+j$2?=y{9q)wJ5?xosTPPa8Q}(yea#f^`f4z2)!T+FB;%z?V+E>?@@_n-2_v}ek zUR?WGitE=P<4)K(dWcMGwWXkbqHMl#fNuOHF#{;+duMjyFD=`*lte$acfA&%5_>Z>b0He4`It{Cu#zUiVJy@aw+M1?fdW znsoJVt2NwDZ^u~bn(&*t_I#Oo?HefTdwfoXsn5BA_@0r!Mz-SP#1$U6HcZTIpB~ig zJ)RwZNGDffZr>vPqEGDKn`&;CJh~jjKEM6T#7pI$A23VrpMHVK@5GkDR@2+pktg0~ z{LCD;SgsxSSMp?fw%OO+b;GsstNoV!m!`Mpy7O}-cKGw;viAurXlr?RN_yq?_I)JfBM#+-p1pQj6D!mT+|`?6v72s))R$ZKchyK z$zNI8zUfM<^$EIjnwQE&;l&~rS6--0XNNJpI<;G2=P=zx4b$VMdpj~;?Dad|RiCfS zKI8O#_@x>{y?-BU9@#J}#MO!+&2{(`XNb&U59x!b*nNeM3YEUzaL`zxqs#i2 z@*rw`re$WKW5Q8AGJucr0E5Z1r($^$H2yr_hM4?sMz)h9yO4kUd@0|LC--z^Q|%E& zYcE;0x+l(LWq&zRs8mwcWXpQ4UfEyka&vkmv@O&-Mc@52E|N0}!>9`PR|Mn4@`&QH z3Ek79DyQ5w`n8LF=8SoVvO|btk@Yg=GQFEh+S7!ix{47RUAg$p>6FAfRpUaV$`tpy zc*TPD?g4AIQ;doCdHXEX_8+Et+tL>OZtotQ=i2`oe+D!1Da#7U?oc?Y!ZgK4Jh35{ zcAC8T`ufAr!a}8=>kVH+^N#J1J)h;)Dev)I~%lX1_dM_@EAC6`8 z#OWqmshXw#altn=(q^D*G45#a{jXi!yEotIyX7|deir=y==$z>s@wnnV-#6Yl9iE^ ztcdI~3T36Tvs*&;CL*KAh-7D_L=oAuD0`KxNFiAnSw+V8dG62e-`{!k=)Sw}lXKqh z_jSFl*LV)bQm(+*U^exPNfUlETFDc^HZ9gcbYN%%5-a@BNV^Su(4bmz8KdY?k+(S(wV z&x#*d2wC(qx+DeI*(}%Rnp(09QQffVa zIDjrO&1+bR{W{}@eF&eO5~b|42x*;GJ?)*hH|b7>CfzLKaGktKM@B2D)9FX9aDe8# ztfuV^k3r^B6393oOYz9ZoOgX zWcXN4;&EpYlh`X>$_NqyWuP6tDD$veaAeBJ%}npXsV8p1rQvf^X}cUNC?Z~G^ChGW zo)E80wCfZ3)zWyLf3nvpxH$RdvF+y`3U3NXMfLa^sEP}fMD?bKqKA zA<09s27$)&dRosa9>~L{UL2;3g z03KYt#&y5|0dT?#K0v9dW(o?x#b&P+e71Ilp`5WWBqV@G#ndwrZIp;H>~i>4LJ$qG zT;K$*6!5KC1sOAF*mucWmHA((XcWEX)MjYwEYYpElQLN$o>`XWO3;-ydNzT#EtId1 zll{^#tD~L&nm#DNq9@&LR9Dy%|JaA=@D;;rSHn`mr|d`G#~XfR0u{ZqJY_`FwV-Qe z0GUn0jrQT8>O7y#A{jPI^`*0eQ{_t>6Q;@1c2~!n=Dy4clKM(s0z)YU`cUZDLanrL za27xy%nDu^6=T#uO--Hoar!2+JWV~U9ic24gQmulCosR2V%I`v31b{sL;4O5viYi< za6?XJO=H9I%_HSTS%>JgLJ<~1VED+DpACrvnxbyx4fhL=lkMt?qBwA^pV79Jr(}(L za3ktg^L4{8g8yLrmWo|f!hZW7B}K0@Nk)Ay6c@ZBIVIyG?oThfLj?&ePGohp|zUI^!i@(T&UW|Mfg@r5Cb2Y?UklVFj0=waD=ni6bRAaZIT z3JDP`Q5ry_yeCrJ;d1@KQnV*a@iR)MkLe>jYv%TRa|ps$FM9l)vh{tKq~lookb#(F zuU6be6Rue8q4y`QIcc&Oj~$5kEkP^Uf73EEEk&`buAS4;;s-bY~hQRvL``re8^Dpy%YI`u@^ktTAG>wxrVqj z20afVBi#oo+eK(9F_y##0VDEo3j@4qCoSt|IB&!WG3W5$Ml;daBxrBPDR@7=QMHS* zuFb~cd$H)^V7!MAW#BniSK<3_2OitjY*7)fs71k{t?g#c(q#J5F<<%sB~5E;nIu`COnPH>r-fd`?%q;L)62HG`(C5IxBB8_ zh4oDIBO3Jh$b$>gYQoQkNsWE#q16+Ha0T2(h9F(Y2dRj7E1V`3QJ=gET9g?HKa3eT*3 zT%CG0&verXB*VSvr`q`tCPX&kB8lOYWX+T|5?&9fIbU$vcv*sHKIf?;B66FrEjF(28#;n{oeqq&1rpcL@I)4D~S#_q9Lpn zPO=+39r2%PpZz!+>zmaoS9Eiy@1WE6F(F$i1IBBUbm96RRPI|xCa~$VF4HzpJ+H`6 zu~vK=AZXX?pG+1$BC&n(`jqs$@>+7guRp#XA~0OJf78Zl5UKev<8IkpDE&KPXpFHi zW`>KuZ@Tbo0VH4!q$c4Guu>|j_4&a&cRZ8)b2vxlr~bMh9c!(i!$bdX5z|g+M42bO zAyZ>2ERYN8Y*fp4Ok*IY)L%k7=j@ND*&Ty>AMDcUyXYq_-u^A1_Sn)fvk|ikA9XE5 zq?)5XcXZ`dr&s5<$4yW3IbeVfbn8Zn`>-K^OfdszWo>a>18aB?m`cnU!|&fuuVBya zMYkf4zHUx`mGj(qjasM0KHt=nqEoVGgvh1j_6^?V?`0vMRS($Oy~_H+ul`{0j>~tq zFK?LTP^S|}?isrYvy9m}+8b)CGc)ym>92@=p&KCjuJfm=tN+Rf5W1=01_3UG2NB5? zzD`3`{~9x3$Yo)sAl@kcZCV;OLwEwuE|8s>BNzpWb1P|Vz4S5 z;Y3yMTX_6#w(xUDX}(i`Uu~C8a6H*7!@ANH%pCCb=hvFLN~bXQ8IFl3v;}Je8%8Xb zdE@u&d9Z85z~gRJS#na6ER;mCb%~Of4^<`i1X~a>Sm(|)Ka5!dTf_ z?j7bXkdovZ;@udS_@2(u`O=bKL620t$B0ql~PihAS2wfH&uUABvb>hbO!6XB( z04(UfSC=XF-XbG(Cncqi1b0Dx#89yX*9D-Ea_iIDrL+wAVKNf8v5V(8kk2ics7)vxreC<%m{_i@* z_i()11T$c!C`kzyjZq)n0huD8^!@y{^^KKZA$kG7a~&2L!2O)+DONbKQZW^7>Yk;L ze=yC1yFRZevY#$9snVf~Jjf!cZb@Zl>vhI6b8i_+H&k3yZ|AQ2a%_0+$gFGZos?m# zVGK{h$B(u-{)pU}$Q>3F+o4(8S-u_Ccg(til~>y@$&Z}r_v5u!rET`gmJj`V6|8N1 z_aVW=-ZZXAU1ZA{l?~aGE~(;BfY#7ef>J z>n0X|^HqEg;ydSu+!TsvV#(RZ8@HqFdBx*~I*j+kr;aO2Un{Z}?>(l%&EeG zfghCKwOdFq2DmrT^Je8d-XXc}=+@f>4ccv*rr$C)dZ8NdA|!kvPvqIBas zTRN|d?<=J0r0lesWF-*7)7HNDG%u)b?JC;2KCofk&snD+Mj(ig%N{yaWL7BOut!Yi zWRaP!yu?<1LJ;=+iymv>UG-wFkPv=2t-+ZV>_qtYB#GZV4jjrvU^?=0Q_PnEa|8qn z)0&*x>4aRwbK<(?JhcJMU7}(zPTaMr=cyRzz$bx-st5=m#d-#r>_IZQ(%~4QyDrj4 zfE{)LEZJX>2`co@y2PqVWDp#ud<m!q=J5xC%}9 z5cIMxEFg?3n3O__a3}l?nJr0;gxO+bYES3joKUg)2e*Rv+@4d1UR}L}Dl(#14a}6m7?)CaE=g~b_HfzCG$5(cQ}8I0ZUTgGJv)wM%y@; zx&c4}xXT*y&PZS2F`X>!d>z`@B!o_5gS{BR^H3gyavH|nCx-U@P%-z)4AypkLa>UR zJdi`}Z~eaM{Cfin%N3IH?LM8pPkbV~t{=Jmyt}Sjq`K!s`sx0*p_d8ehATmO?~NJ+ zW5j-pKX8&hEqtR%otam%T(UJ`oH^j(AQypKEV*BJwb1+itmWeU_redQ4qdF&v5bqq z;JorR`hHhn;;P!}GmmG7Sog~7`1PCgnWSs~UF%UQwPSAtrBpapE$bq29u9_+g0NZP z)pM4Xqo9Ql2$1Rr*<~8>Y->~(fPI5v>mFdVpxD6Pm)-CmIZiDvaGuu zpnyOLRlCodDku=bK3Icg2YwIxbD1D(VKl- z+oUN;fl`1vMzr8yg{Gpr)ubEk*Sx31>UAf#EwP7z_W9`bz}F-Mhew)R8Uz9w7p&jV zxJZ1fwe(u7%GSkRwtKK)q23^!E>Sk3E_Cu_#iHx}7s8ngXs4W#btnnWV;CGc^A)Z= zBBu+wv;~)^beThsPO0rMNza^e5l;zCKbSgEu=vO)E8^LukGkX@=Z;7h9-U+Q)9X6y zdaN?;b)3i#7oi6^We^i4yak*9zMxvEr!+eXV^Qv>q-+j*&r3_%e0sWT?Z=K>0s)=^ z1o)(=ZT$sT0oIqduMiaM?0{y$jFeU0cjGlE@sO`a`t8&sw!b$@habe`m$4>_bEMBC ztzWlAia)vgB5zbz)RrnwH04Um)hlZnFQaY>kdf07(4RTraz915q`SH;`I%cHa~ci0 zAFR7|sBdN0-y^`z_Jd}&$$6d!_^q*6u%0&9Po z>rVHd6T%|Z7YHPD!No%@E>rIwThYD3KDtrXaN?QRt1R*~L#dCtv9`X-aklcYMor60`D3!RR0w|2gyeGY~-Es>3+UJT(RX(;~ zJr1Iy4Cr`()bWK5SGa~5YiSL_E+^HunOg5;_ybMA=9y2fH|KSs!GX9yY?ojM`NBr( z;aPFi<~M|2HnY8VGzJHI#ls^i7h#l?&ran|y?@2%i#_e8yq}+}cBPE6*(ZJqj{Cx7#FAnIz0b+=M4}>9Zz_cKu%UN%F4|{Ve1>!;IfPb`|92 z*)TKhBuLrGK6#u3?TEh{lJY0iF#i@3G67Y1uck2`79J6*bdWQ%Yt&xp)tbiw>KB2S z5mK)x{NVomk)<`9_$7n0xHD*c4Gau`4E66v%z0w8@Lc=>NljX25p#Hs;|M~S`t|9Y7cFE5}Mgd-5QtU7ApRNT!AJ)i&wFAIiR%JCT-;b zL7e*e{h;orpPZ#g+2!grEqZpL4lDHuH|;kRI$-y`^woCG6AH(85&}8ZKf1ouy!E`W zccVCB=5^lr%Fl)TatI}m^J-I!E)=Y}^tjKyoKLpW@UaxmM-|FI?uc{_ng)`m*rg*H zfK5)6wD(0S2)M|bf#kTj=X$vy_7)Ne9}t9&7p=D|jnUUR0=VW!RnC+4JwSrKDJ&!q z)Uu-4rvU4@4NN%*QD%svfc_8&;;gse@P%`ZO;YVpG>F1?-vKg$@Vms+`yP}K7-nT= zT9p22K#4;|Pxx@tr{eT!SMF^THn&^)D3|w(>fIaKp5gx}o7bHZKoBRBZ+?3|dhb$T zh1i}R`}DQFnnORSzMZGtcp6t*Gxu?pA0K6!$&i%RchG_I3-jR9UENshsw#;OwYtVM zf150u4ENS*O1wE4^FPeK(~q;7pDqJqher*5i#y+ZJ$`=eQU2J>v8kYxR??q? z%#4v=re6$Az3Ts)HlR$1inFKfi9S7gT74rf+1n#8xV}MY<-B_Lr(H6;{j%IP0pJV= z?j6qvaJd&VbweMC@ZV>uu@crw=l9e2K3j~}<@gBRF z1&m*wQfU5IEg(Vu$R~3-QJb-4jMsr0nZ%;&P$T{9#fv%=HV?hn_};U2RE~vvSR)yAqU6R~@&gAAXhI zzi?*co=ej4Y^%pcG8e(+A5bkt)@aKQDZ08Pq-Mhk_m2*IDEPOO=yQ1TDLwIjjOS+_| zq;tPC_DfF;;OOvgUV}U_svRU&Qa$6JDMIQCFjfF3X+&xZcf|Eq|HJ7}uIYEnDxz%l zm50pl8#s4eD$do~eWo;5>L-QFg?X8iYMYrO4e=BweSYK`=o!XlbJ9vKti;VM-y{8M zc4|>~UrlOad4a2!pYb1<-ffI$`Nkv5p*SRn>W^H z@`=(j;PvJR0h3QBb7QuS`#L|92gV(4JgOo(!Q3bvPlooTsa<1WkO$J^W;rxT;Z(^SU`~#T- zT6ViO$vPg5^KYodofD_H%AHFaK<8TEcWj)gU_DY*l=ha(rDw(B>my?$z4VOqcN8Xd zNB@YAx@%SiemTiBxX33y&qMu3$Z%4I)Soj``0APWln+gcOiP|jE2XUmkNpK3t~bvF z<1ow?KnmXn?wsp|6Rs$aP&A>%_xn}paH8lHm5b5pZBsNoFZxoBQa_W__b14udHAdL z+#ijacdfH4uJKD4pO@(}VvY>I zqG*`iI)V%!=3I!JF9TP}9N$$&OG` zU4GDU+;_Y1nwP}{wtKPb!w8#-y-#EpFLcM~1^d1F)up^ZtABdw$f@dN1uHbhtozl; zNXklmisPJeKY8Oip3@5cm5^)QeEdB0;_jd9kG^E#+m#36tU}5~%tw)Ph_ebo6oZ>P z9a~=LatR|P`HETO#TRgh1tSly&W;Y`vEX^)i#vnE0uhYqT8<{yw*t>;$vfjYbfoG# zqL?WBA`5IR*geS`6Xhmz7M>VQ^`xmxGEH`bF#0YoFXHQC9AZ;=qe7EGXL(=aBSWUw zX=n0#bjKC!Ot`xOCMPN?idumP&mSxi3L&%42!gW9D{c#e)_~9&%OuW6 z|2p-&d+U7MNO;5gfBve<-g!c#I%M@~lx=TqTsl>1GgWG&*}74%ro|NxdFOp*e$uZ! zBJLg*q+5Ge7m*jPmcAaU{ODckmW+&lJk9F~X@~1@LBLK0vdbzDFEoyK! zYiWU=WyBaB4J>3q4uOmXgkgk?ioQFQ>ueuT5T>Z+%A^?_TyYMK8?r-xYC zLif-lnMAPt$~d3hVr>2+ow?jS=za>mzgg8Mv@4?*m=0hBODF8@uiL4(3PrI2Kn-m@ z;99UH02>Ak7trDVunsl7e|W-G;0y?+bO$9fJv}`;J39wQhvmC}*V5zg6iG%|>~}JC zDm%Uzns4R*?R{cE*J|qwqvW{ioY#lTWe)7egoQgW@N8}6;S386eW@ovASi>WHa#|W z`lrb8C`T#w8+z!0i?h-azHg*_oLONGzL|RgYHmSwcBNzlth(A-KMdb7g}?gW3qD8c zs?Q%~`~49xQPFy=OsGisr>FQ{kJPjIEQxXVESlxoq?go#AMFr!5T0Pkqj)6vbmZCb z?Q)|Fsr!GCI2lR>oZppj@JjRQk2wZeYnM8*6W0Mm(@Gt;E9LZW`qag=FM_ z6c-o(=Y42TxlSBq)9l;jUXgUV%=2rk=T|t$j=d0cNRMUtd;Q-?Jm;AobV77nrZXRB zJO?LN)`@dJsLhCpEnxMl65X8XaTRl%K3bp93Ql zG9Lr8ZW2kGy_fAjG&Yu6LCYnK2~dRdwL_Q6yGwocDfI&(@!b2T^6Z;#-eac@4sLMC z*|P^+rvJ`G|0OeiOyZgR*Yw{H^Y`TLo|3DG-68n8JJnIJ$;8SkF`eOb^)J&TYRNYa z{l7>QqjE8dOCa|1@}=N$)t?g+d&wE#e~i?HI7P4>h<;}tb=m8v?~0zS zf&)c_(_+S|`OB{T^9i>)=sMZL=&uJl5P0ifP1&- z0^D%8VY0OZk{Psp2M1wZ7y;fW=-P~7CX>-I@65|Es>RU*P@RXpB}Qut1b z=5T20u0TEJ)v$-De@8c83rSmeUeO*pqf*+aO{yr6mC1POk^|$g=8(hFrB6qKv;c>y zF1mV@YQiO$t10N+axY)kto8h2F&Z<}rH19-CR9vk`n;rDqxY{`O$I-Z_%`d2t<4Aa zAkqdRq9QvxTZX@k2_h_}2>b8v??p4^=hE8XjlWf#F_-rIomHn;F*m$sQJ!f<_q8?? zY{v?%{x?!rJx%mzFN_`EUVP4-y82@J%~O|MG!D2)g1AWxCF2k62dHXBLjAw#jmUm| ze|wE4QYs=9YZ3sCORagAnf>Rhmb4)C2y*91oy#-e2cTCzP`7HM_~I~c?iAX1>zG|s z2~=skPk|SYB&k^fisT_QN%ST-gtaxQY=Ld#||s zahC3t04=I-Q`b8#cMi3UIIv4QT?K<3VFe2V3Jt`T*sDk_!|lK8wa4}^T)OBh>pIOC z*~o@e9v}RKnGjP@ zEt1pf&qcYuA2<0#%~o`rlR&4#*S4F_7CG{lCVz{_-archw4n1^T2GE&Yq8mMD$9g6 z0@?-02}93h;H+>x7mBt2;kE9@KU2v-g-HT^hunn14hg+GEjGrn*~fUVonBE;)c3*M zj6-(5GJ3vZs^S6>N7}Uwz$!_^(N^_h-RzT_`i>rd%6H)8F#uox>eWL%u05Qfd#7%1 zOw6rZD&4B5re{rQb$n!V<*&srdI6rU2CCP`Np0H*0%G6W8%+I&B4`UXQ@?EkNXcm^ zz4&Ia(}0|viY|aFjsSEqfq(*MrL@~sH>ak)hF!#*AibuT89)dbU)L|SH;sPLWj7zb z`@xjel^rtLQp_^s;I6f9#H|@_Ze3pd#uvu6{3e(y`~~G_t)@`7Oz^9%+1~Pgm|ftWf2F_x>Rq6qfIKRm z)Q)5?v*KVAFC}}xmhPss)Q!x(hP@ekKR&klAGD4Bvj@}?ckhnW|Hgr%8~ovMxW_?7 z=L<^SW|mQIa>Y@cQ%XvD@Mzb|#R~10V&h3a!bP|*`QcCgbZT?6@pxrUyx~wwf2ii` z7kSixPf|WBZJ5E|hn!tJCJ3sWx6il=2_L~G1jnN4plN+$*Ye=b5Bqx+dA8#J#VC7^~M!Y?Oh%timwxcs=VaNfJf zWYX%#e1g_#*VB@ZnvK2rxpQ8ni1$nDobrEo(}Vd*!{+%x!Gy-&5Z5}+&u@+Cj(rJ5 zf<_5?bvT!Db#lJEnk_bGhWf`t=&eG(Gf#L`9;#v(e-j07!0!C8#SLL>%KdS|TSD`a z6r;?n(k_7P?n`OerUqp97@_;}X$Smuq**wrz)-d%5R-Kv3~Dh5jH{_a@WHCd_hdN+x-yE^Dh#{f8uFYXU`T)e?YR2#kjC5w`xv_! zK*8@#Qbv5&*}p@Pepz0my1W__ucM5ij$;iy4)PE~)tmmkvf?@iDSos*r)#%2j}grA zv?K;wDSC5W9XBE^u50n>15eb&z>bkj6lY%jxa8$QbhM+`|@> z0cm}txG-!JqQyNWW%h`#HscP06ng|H`Yylv3qYhre}`MYd3+HVjZqBzv@|r>Pgs8j zyT1v_^Tcih*7{|96Wrz}?w!UuI*g$iFuh659z$CQXCQQKetpVnMJ2Z4wYfwki-t8e z4z%n*mvx6U7Hf?rfe_P-_ozcDq}_eT3EN?bdEPAbhza)dB8S`Pl23|Qn(Kb8kPii|v>YjH)JINgFDyMSdaF$ncYdSYy z)#CEgQ>1``t3)^biO6C!<~4^Yq-_0)wGfmnFkhPOn~h>sW=meDQ_&p+is6#RGqi zXyT02XFomtTgw8YQoEBNMIUhs8Y@#u{|nGW?fSF;)PisPwPdz@2Hcr>C6 zNI+~(_qZ?qH`^nQ{&daaA)&ccIyFANgP_eH3ufbqZ)~4E#7{_?9Jw4<-!Z`dRYu-? z3-v^%PUfjDpSKMohiK=(Ia&OrgThINANA8mt7Wh1W+H*qt9lqz&2SY^ekGDpB!8jY z$?Z`)bpv$VBk4=hN$o#DDgHg!ErQr0P6u?C_SQaqT zG*H{I=EC{e5*adJZYb9Qo$-_(LUZzZ=aXt2wZQR4LQny#=|{UWfq;q`qIn$hzCMs8 zLE|cW_&6xDI|*QYb?`!{!B*nm)MJst@p(jU{IU37M3d~o_ajG_uV)BeXjOZxsju#Y(7tjJV{I3eT(D}bN7eVm_tw~~` z%0W#-^KvCpD*N5tsrG7+{l>?ke{Sj?B9fqi-bqAQ7&?$ALoV zdi$J`M?i;Trkq@|52<`g+J0Q~EG#L9%blP9{KwtNlQNO{V{heG)I+95Mi9YMx>3ox ztNt~%tL76qPx$=7|Ba}?C)OG-amxt85Kv|z=K1FDNEwxZBN?5U`G=d}`}@OwW8wV- z?F$zyM3{-;M)0oB|8Kwtl@vYNZ@-Yi;gO^~|4hH!@ZRnBa_CeHV@wBi=lsHu=t0Xf zmz=w~6S?R0s$1&}b@cldj>fj2_W&Vzyigv2JIFtrj98BMPM0)^Qw`h!Q}J;ImV37y%+eoT{KP#zR>M z@iSC<%fAL<1PO$wCr|7olBC%?kl|FJT!c6qfaO#Kh0Ko<_QS?YwP-37LFT;2M;_zA zJ#D66OtF&eob8``JQG~HT+-mJxt+5`CQepY;E5BwU2SYf35P2@i}#+;Q_uf?m&z@5&MpX zjbOX9vI6DIiUC_wNaUd@F2D9828S?&BOX0slu0>twJGXc%;DQuVLc&NXS&ks0kfnb zK;Z(S{amuS(BOff#S)Q_$DyM6o z!GRSo$^+li@Nb8wa5pfY?gwrafCXCKDmJwJweK42UE&w9ZaB3l~<5%=$(Q&XEp z7J#;6YS|&u@?+`R%0Ra4?dcxfe)qLuquH} z9Mbrdpxa_8RMgZE)^$dT+}5VS5!r{%H9Q$=(S6nF+fj|&f%OGcZE&uh-T*gQDO3A4@!a_lW#m<<$7te0+rir+kPYQD|;t zRWRv4)#g))U}xdrsIjQu>G6j03^ysgd$~Iig8j7d+&+_@&qGC(HQtgYrF`m)Jy{o~ zh%T4zb40m19sb$A{BKD7OlMkYTH4z+H8oFqEN~43`9X6PkYuF0)%^I-W0hY|p{ zoT?}mDOOxd@azQ7-bHHZ6Qbj32rdkzfY$h!G=jHcbJKhCp zXdNbv0{IebLzzig@dELiXH#@Ro5mU@E)E8U-{Wy+y*`JyCU24}vZHuJXoQ?e%wbJd zvFkJ&Dh`IA)*^Bur?vbL3T;4Wr3Bm(9+PvMF66LE{srZHh4uMXxn>e;sy z3x>Df(stfceYM$7ZozK19^CrE`~`E%7kPOqaBZkTURrIs4G1JIj$S2H=30S436$Y+ zWu*{9k>6g6tgZFlUKH2Cr*Aj4wGrzsUH%?CCFJHG`wLrfYSyBlW^aVa>=5*I(Dg`@ zR3(g(OuYNHQJeoe|Lf1;2719R*7&O$)>o{$8~RnO!JGMR!$oO{$_#Z9ij9rA%6a55 z*?HLocyyacN8tPeDlARNVFyLxC*QT`ht5+4RyQIvuJR>L86ABc&f{WY`uV1}5g#$> zMCc=nI6*jZi0=Kv_KupCTCX5?-jp~y<(H;z`CzRGX$tMt*rnN~X!i!% z_CV$Q0$W>AbT_&}0YcPTXZ+vn# zG^Z8iF9Mw}@^f?>s{YMYV!QozQzq!}$lA&C$j78NNeRe%E4sb&m)$U^Emuw- z9n9MkAmbEvtj6HBDoG^yC${W1WE_CRBiNt%-~1QcqsE5y1wy(^${hF#II*1_9f<}V zs#wFQ>B`v2?mYQy7H!h<=-Ty`xF^+j9#+9*^9@a25Si@496P$I2-h=hUNj_msUv6_kEEEtNv^(W&jn?TwqZ15U-87M{aDkkn4q85Z|eX-lD z5%bsT$9I-@av;Xq{x{;Yg+>!5W&g`~%*Gl{iJ6@L9~A))&=vZ|;VNSATfl(})!eQ@ z6ou;JVN1tah0S7&TcfX=#Fz@u?{~Ntl7_FrZM6$skwg!^deYvxdMl=y*cj(wOaeC-wnXoa+FN~1O=2C>sfpD zMnJ*xva_vlfI&FTC~^l!L*_Hd!gV#0Vm$?gw|F~Isx~2E4HXMk=LnuV2KwAo+m0pQeulw^r(f!=}uWz5Oupq1OPmcNKoGSB>j{7~& z6V<8&5kHQNwKzpHI#D{7OEQQ1Z#Nn*;<96nUipGZn6qYbN?~8bXOm_hA^4STZEd+# z9^gLo@GQR40yNmsp;(65g&Gv$%(Zd~M&3KJr%KwJqBKMv11U69F^V0!l)ke)HbiVF z5_EJ8h|L!wrUGSIf8n(-13^2Kw76HqU&v~YMRaLi038rh3R!oQ1i+=m|UHx~ZFy_c{X;(K^@YbkZxBk#xDz?%tNeg$d^4CxRb!7ngw z&TDb+r2jG!I8JPdhVrfVn-FprK$ zko|8eZC&xz|JfAoH8FOB#eL%jO5sf${_KtLLVt~f3Y*u>((<4&$Bs0azi+luZl%Z$ zZY3&I9jD}B~py4N^+ZZ+Z0y}B+YBk};6NV?7I2WIQ_=JdC^Sgl!u@3OV z-N6uv;^Fcl21va6vr>C3RBi1SR#&hXP zMw$MLjEF32#Az7r3l0tvoRKZvB_0R)GHz#!qKIBRV!nmCXjO1=vrtcAwB z9_w+>Cld&d(@y13zybFiu551%p)j`fs;KDEgl7O3tGE?C8yhi2XA&~-EPQH^_Yq(QF13f zgMj8=h$)xYf8*d5$8*D0kYT6m+=(au;NCq2%9W|^BSJ#$<|3FDqh>?QqOOiWZvLCd z3HkHY9caf~AAQPnBI67<2Y;;mcl~I@7)o8FSyj;Fh4vT{q+K(fPlzK0u+qF%f8D2_ z#&?JMAEZai@qejqnG3cKzj>`3HutYtJ_lR+3Nv)^bkEwKqWHvCb}Z{6@yR2rr`m1! z7%iylaH>0E4?Krre&1z5rqB!y*NNi^ZJnLxb9;h=f{L+&u^N$e(*ju<8k*BAAV|0q z62c?bc7W+8-O`o$!Qbk4xM@_S!uso)lm=uAPv@MjepzjuRtIfn3^%cisdvMs1Vx3B zFWLDeDA=8*EH}=>cVM9jCL!DT=o|1CGyaudP*~x>1Uk{podvr98*!f<6+5aI#1T2a zAQXU*pn}w*mjuW0t4+9EPqg%@re=MEPX18+E%Nl@3ys5K2^zX(=e5OMM+t@GAd7g1r7Fnk7)^0ykbJL10^y#2$bh2xplT zrKlY>JCH|^k}E>YinUUTl(3L@v5@gJbEHSW5s&xI%+Ig)q}ZnHvE6mR_CR7qqL)f^ zcgRd_?b=Dr?h{KCNe+^eC{{2z+=@EJV7I>?WlT_WCP!H=fM6JX9|M7q*oKZQ3mo$R zRK(PDn27#Fc*iEznLKxp<~Sz4mwc~8A7T@?T8IkLO;1i{j~Mf@D&zZp!TZL0GN9RI zW21QGM(=)o*O{2Qsr_eUb!Kq8Y(w=+dlADmp3!8-aSD>T{sNwQ5u{7G33Wy#wnt-{ ziGU`wThQQ5YA4QdV6<>BkU3b95;9D-M+HS_x?dWKWnEk&vR=`}K&r^7j1GnuMwPL@ z)XJ*v&>K~G6hO8ZU5y)>J5Zc>tq$Dq!&`CYP^rS1fSQ)}E2?sl97?6^vaW2Nu9*68( zffqvG|CLE)$#trj)^UtqCp>-O&jeBqIu5OfUa4qE6&Qem5>Ah{KY61gO+Dpq&~kX0 z!jP$+?Jg48N5qojkokgV@o%nHL zlOWxm8_M0w$WcI!nXBpB_t{sZkCZ^TW9@?0Ax=jq3l}vU<}yX90`Dxy(_wyy^NAHY z`-zGCXM_X09v!`e>>Ku-N9B@n>?7a8?}Flv9~v685{_r#qyG%*Von09$)FoVm|l0$ zjjB>AU~LZ<^{|9DU~cwfVuB|yQT%2hzzg_=*4i|_5%dV*LWlbZ_W+=rR+giv>F7>5 zd?zSPV(XTp#_&V*L+$^v#@F@x$U6-~JRNJI!4=UQ26w>y&m5_#BQg*qr7M_W&4UwG zZ9=Uu`LUbLSb7H*W%aP?OSUM0$4iWO1MAn5Qpte;~yoOJmTe z*I|BJQiYW9JlZLa)Gz-Ap0s={xKB0w6 z8PH#JbMKi_8L}_LTi`*W*^R)viDG&m)d%RLum73f-OZ`bo!e7^v|zsGA3%p13CG9F zC!W-cv<)iz{|gX!j=$)mVrV#kdLQU6bo^1ih}!mxWL=z^&G5O5^nbVf#%mlUfn5mI zfOjIYm4=b-CQQ-d$|CXb4YBN=X(rJtL4O}wy;j3NB?HSI?ErD&N*7@_>$S%-}M$sny zzp@cJezO&Cc~G_x2yaWTUB7OJeUHo(=66V?5b8jIm8b1?)PoEr9fzC;s$1w{6gfT&P@((BQ)DIBQio5+fa7JyPAWqO`M!=du)L}#iz z+8}rrXeZ*VuueT06CI6|pDz5?Dcb)Yu*`>XCuD-Zu&>a!sC;L>u(SkX=M|#rg8Ljh z3ya}|eb!aORor9*5j2ASq6L|(bpwbpq9Nt-R7_Nqw*6h(17yUzs*nQHl~?Ew&JY>$ zSTq>7U3>vU35Z=I322vfnWSf67?~x_RYA5&?44f!iDo1K@~~DaGP2i;3bsU^jovG+f}UId`^mD5$|hAFq5x;f%3eOJRL4k%(u83g z15#CJx3aUZk#&m>>?@l>$&V0v*}n0Vo3@Lz`Ujy8Dhxuz%KVgcoCvriX|R|I2?uBO-#6 ztY^fKpNd*kC7C}sWOI<2r#Vry*MCVkATw}Ba`9;L()PjrBHOmv1nGbd;_khA!@RS>T6QcHwc+UFz8$ zN)6niLUvq-gq}n##ZN}e&U^4p6!`g8i1hmNxUX$qkqdX$3Z!o+7wNd~to7izV@JgA z2Tdjc9>ES5r6#xx1UifYg<5M9xr=G0I-?XH@I`SKKj4eUA6X`ug1RysI6_)!33t3H zsfe$ft7K0mP72)+*X$M-GWJ(SF-KSWZ`Jdqk8HBB@R4&cA>XCxe(Ga4-Cvor_OA3D zUk>HjzL*KV*L&Y8rQd|lX@BUX(gSy%K#wyb8C)uiZS=l^Q}fZ{7U%qeKNmigKW$}j zv7Vmw@$#S5q%tx*hu+XPdI80Il4H%=4%u)G?PSP2l5qU~%+4ucZkEi2oeWA3`0g<& zca1ufmXQe2J^w&!{E0c3W?`<9K=|y-91O~L{CASiPAKm<>>SH3vumldUTo2_yAXewT`{bV*-<2t(q4bl%0uIQl+hV$fo~7Y9~^Wq zh!>{+xWlqS;aBfDA&L(!BpuT9N`fyO)hvxaSv?o;<-PCJGWwkgvnqla`BeLn`;$sR zn-7K_bC;$(t|(;a{2kxV8}6o-@xZ@6Ioc{tJeWz9OQ(^d;N>0OO~X&$j)vvFesWbU ztHbubnEGg2v`qe5g4>>#`?%Y9?-#XRmS(*5tFTwtP%zTGOC29^iZExiz+x$5c2hd% z^^K-mH_pr*xNgXRqjT<;Nnw(NUj(6l1 zvSrQ9MW6AiR3L6h${}Z?zOx7PdmfWnFOj@hVpZjvT`ZCCyj)p$_w;aReVV=RzPI#8 z9IrRePB8Y?KKg7%Zj-J&@+c>%CtosadfHh(frLQ#i_*_E@O{F#{f6PKQ(VuMzGj%` znnf$8T)lKyAwFA=dGuyey=_7h8nszgUwcY*v-n#xyJo@KEv7vflb!L+E>nZ>z(N zy6T*rEZeioe6Ge~Yk&GEBC~kIt4$cP&z{-8b6Wo&TL~7a$z8)og<+)v$jG!+6}*;2 zndj+**AHg9oXGlhk$LrrI_vCgsKT|grKgNbwQmjEFLN3&{;m-j0DY9OaEPmVZBIZZ z>v#?C8#6%~zi-(Ge$6_)t-qMGJ8`*w!z9g-RPvRRr)sywbf z;QOocxsP4gKK_qHuz`IX?rIu`|MJfR@U&}Ko_Q6#WaW6;zK@h+ulnDwwpBVm%SO#g zh$$C=kgdv9?Ww9T;FVPPMt9|y{eIFX_tf`Xqw(NQb4e+s#{JTc#MRSft;8F4_YLaa zX`CoGverAIZY-JhhnNMil1;T!Q%2ND%L-BbapV3jdf&?AsvCcYG=oynQ*Nbufy6UC z?HPUsDP7#sJuU-g?+^OQLy5elDYVD_aw^?3e?`enr|(foe4Z;iG`>vkkgH26yiPU! z)NtuRsSQb_#+5HGo<{HliZ__e7J{+tT$cP;;Ue#7J9 z9lYnk#JOkhvpt6^b#ckr`nV|+A+4?q+bhQgGqCuOWm+7yeZE0E#oYPmawB(O9o2pd zx!G56o+Esg?3!=45%fJDyE8nZDYbb|j?wW2wh%|AAxqwuOrxv%)D{fAWg>B8?otjR zY|fX+KbX@5T@u%l6+NQV6g+6~K&Y0J>Uqn&VMFtos-`9S)A6CY+<|AR&Neg$-snkA z{BcfNAZ}sLt|J$mt@q_#d{bKM#SvZQ?rSCb1RwW-C!?;7in$ZJxXy)h>frw|_uk=H z_VNF)N@b-hJ1b*{-a zpX2w>a~#i~kK;bN@B7GgUFUgTpYi^@)?0o_-Gb~<`t{?nsB7>82R+aT5dH8P2>Lse z_iLtvzp)}^3991wt&>KIB8O~{ipm;V7c@|JbSy9tUtN(QyLTX2%2t>vR{ia`O6f%^ z*3ZY+_NLC(s8qLmX}O zkcy3n#p}c0xQ?0JzA=3->>q|*$kUIR4;JPeOvpAx#vK@1`x z@s?z;-L?&d2bx8_)`5z|PP)&gxN$7cP!wMJoNfSnBmhh^g}&x3gk5~Rq#wxU4)gMU z=$)hK&|qm<+&eI3i@K-4@N(SK^jf%In3A`OBrP57f5gh_?{s}$_rrMD_+)6ds!1~a zB$cxF$y`l=Hk`-Von$D5PFxR>QEjyJihfDPf9m&y)z|{DNZ`s?+`(`QkwR3anm85D9icx!j=hZx4Y4=Cye z-Q_QG9y}bMSi7$$m+exL{(VcbN4tkfvX4dZl3(9Fe26w_CgQe0lu)`uLJ4*KM(X>g zBPD3a2i5=}5YE0+KpG`A>g($jmOCz@AVqhr&L#0v{m61-J7&E!-+S6EgAr4*ntHr6 zId^Rj^Ovx?;3Xruw0GR4p#lx%+*S6c9S$0VWzbI&pS>A<7XO=0-PfnQ=5&AJ`}1~W z^jt%{d%xu#vcGDx!|F@;AD#=L0$EJ{R8K!B$6sciqP;V1&8~JJ|`JysGZ^iuUm;(@sKcsSgVZnsc^ZQvfEhM=t*U53U0oPfaHU?yfbAyY~`#{Z`gdx zYrLzjFURe0{&A)(b~v^)tWQ>LU{6CJTNs;Ca=2X>_>+pGRWYwF9le=nd&^XJvRo1d;q`?zq1>fbx`YKu(0GqC=Q^E)asZE8j)CJ&q!f#ZVZc(}ERV-4h4 z8C?e**quRW;e0eR_|6<6x}~;rz%6w%5<@xrwzb9Xts!KHnI6z7_$)>xMERg*dR0=% z?|pP*TjTzPfb*Be87CufU7~fLPPl^9Y2M%cx#zs^JEt%#5g74NHb3FFJq9986HAWSSGro(Y=_^lC ziK7iJH`j~nKU3C9xe|Myz4*K-#Pp7r$-u4rt1wp||8(ht59)@lAIc@7sGWcL71QYx z30^ER7R*H{cIqYUT2~8p+@%olHsB_jlr(ym5|m`?jO}l&?dYMD@tQ3eHW{M5P7L8F zKKhlTSC@-n<=PyJ&L-e(VKc`c$cT_Q?k|+lWg>N&3zWG|25e2 zVu;@viYF&>pGENyB{(nI7qityWYV22v1{MkJfg1ogrM`5YWu$G7u{M*KJN#mgL-jv z2Km82RSVlGCaY?gS^3rAq*SDb{el)0hiKqRXh2+CEWE9dDp|^Wtgi3o*v&2N&0i@~ zEh23q{pVcD3oK3-otAMg2e%uV{n2+Sac8=pW^$wUwaXnr@~#2~Qua2tKR3?TnWkl% zW|_+LoRn+WLkNDaWXN9PRc9$;!AKVo0$ku~ZI)=Ak-do5VW&QEav|=cl#`#741tCA zsa;*__bSz`KKhngILe43e%{1m@raOohXu;n z(1+3M6AgKsw4W8PItPrN-KV=2`?B$2e6>IT;mYAIpI%}_{3DToA}<16mB!vnMH9wc z6QjIZZt^!Rez2bqV3s%AnZo5#9lS`dPi3$D`$I>j(eFq~ky{N}s=*fbzE7oyY5BGf z1^-ccp}D&Gcg`GHc#!P@z{(j~Df%mfG!DqBz;uO&ha-U^OGoig)F0Zm*kB<7GrK;= z=IOP1wOX~^vzF(jX02zQTb{dp%l2o>Z};-DKeQ9`tF&|KrdfUQwq?xm}`R zSOu1Ou)f;X=2!+lA@MS!5_Jfni0JrwSXa8*R)Ci??ZmM^GJzZI12sZ;szfiU9aWF4 zdB|^Ze|6P)LHNxcT@zR?*OX8lEmKV)qqHN1%~|H{Lm$M=$Og7*_D!n!ukxuQiIyO!ryWR-gVpB`pBOC zq}Kbjcd1}y`pyi8;#rq+N$vdytz^@r*HVUC2KC-CKlMNO!_TbCjC+=#&=t%h$Q^gd zJ|p7yO+|`3@wrB_x|!;AjmdGkzx##8k{vX&$2)X1g~yVGHTkYycQ6wlxhAa7H5X0~H8OaCZc-OTeD_w^N% z5OeMPDc*BB=6461i<-&z9@O~F!ue}rY-08IOi#vuv8Zs}KJlAlSYH)>nr{|Amr(w| z@oqvog3w*DK}+UBMwP-|G7_YqO0;0M&q(3EWJrk5BK+o!JL6~Xk(r<{^3Z*4?zVs^ zxx}{Biq>+^+iEL|xm%z4&oA&k%>`9BDCl}{VBf>l754fjFipUd4&0xAPx3vjze?3- zuNOn>U8oUBsSAGl4px}b&I2qf>~LLM+w*M?uHR4kLfEmioKjn!h#y&M38xr`2~pfF zUq^~><_wzcu=t_2%&vbtQ>2s8Mr7Oa9$$O?K5L%yH`;FVoUc}}=uu^DR1h7E)vi|C z9`hd;KWduO(FCzqdmMLD-A$#~t6cFTb?OJPj$W8OXjE<2RX0Nc*U8iWs|9dBFC5cR z-I}z15mNC!U}0$9e*Z`OBRqX^F|ip$h_D~I=7*Dj8QEl(lf~f`gQlFH1wX|b#p_Ru z_^|mX+;pSOcI{N7sbkkYu&(yEU9Gmk$Fb%i+ooKH1VdFfSzl97uQ#uON@T>o>#vjA zbn;M6(eux?KV2G|%fe{bCvYUJ&V3+S^je8#k)|zUW#_k6R_-MTDoEo1ga&M;$5tcL zt4Tv6(-4VwT`5`zaGtVeO)mcCdAg7Dom`_=6tf@opP0WJz4LR&SB?*eQ*{vWCGDE= zF<*ZeYMnkt96Ef_!Y+aRn&Lz{ib5569 zen~<+;`-mgt?UNRj870HIAaF|Ob6+`TRD^VE3V2;s3(c(OY@cGKl1aUG1C7@lg$p3fB$vfR^ma_4TH5@qgfyB^DJ?Eqm~~d zABlNnnrrA8M6me4>%vfPudqJvrAq#Ax$|J~^MRMQG#Ot}OugH;_2SDlC322jzFUpe z#?;(pom!g#TkgtpGbKf@G75iY&R@!ra;-Gfot{Y|n^%+KlbQ>%VW}o&*RWKxwg&Fn zzq`=9RX)}Ei@rlfu!+!@xGPB*&5z+ zVVGluwaPuUTav$n<2&A6TyG}qa8%p%y>-d|r=ChAH<2R#`{DT0adF|rxy4pDZM*9R z-W0wn4ATQ+Lh4&cG9G|(sx+Mo|AT&iP!yEhozO&|qQJ!O7eE>0Tw;6cWdC*8rpx9o z)j0WK#R2h@0rWFBLic3=xoDP&N^#KbrjdQFq)OB(0T_79Ypwev_KwIC_G`Y$(4QIH$qTI2;WQxCM_fj z3U=TBBay7k-Pw)L=**53x=e+|p7ZEjKY8>De=z4z4nNt{(Op;d$FfgGy59b~Jd;m# zL(cZT{3k{|?lRcD;gqaI#7ZznKj+`kiyY6?$51c7L(EiAMrOu=)E2Hvy~kM38c#<) zO#N%;IB99yt#cRM7ok`+Z`cJv07RXaVGC5UL;^Vp%BG)vO9w7{LOL58h;YOd97LP& zu+-EXaFzeS8gg50Q+95#-=jRrTXfM$jI6`y>1pPNObb04zna8#qdrYce#^b;Ybfe5 z&8-waI(kuK*Iv%d_DPsVCufvSiS3-6QDgL@9`|l>f4vm(rz^nGxyN&hzt(TA!yhOC4Ok1Q+G<%V&-)XcKgQPD>i@ie zbF`W3SOnCR5CWhBWLsMsq!bheyxx1j$Q+VI7#DPRX0T1d{|bHu1_hEj6uNCZHg2M@ ztGXTNFwlCWsSG{|nI*IcOzM!qm6I47)GoA{Bp4 zN5{uK78(Q~B}a1`5Gt<0oO0k7@(~?K`~r2c@HBzD0EG>+nM)#AJk;7yGne1wBMI+- z>ElA-lObxoEGa2@`*t3qbFKT`W+(L4P>YyV-S4?rg9|fvn|w_q5}`QDHZE??-<-Xw zm!Mdu*q_-iPq~q76ukY^3moi>_f;gBw|fZX3JvahV5~l1?>6KrkKhK4*m`#D{OSe) zbhd0Ctg)2jA$n}C-5eUif-bD^P=CgXWPidnKB#{2sEn$ump*r>2qWX-xr-U@(p?x~ z4>3P*Af0M`RSzx2J|z&3f&Tsrx8H(_28Bc22o4!NM=Rf7{)#f$_kc$UWXe7kW)C-( zNi^RAgVGN$D{-=}4%XIFP_v<#Z7v>0x0!q3^&U*A7 z0ZIo#kdVePX9JgFfC5>U^6L=kfI}A!kR8xnpi8$jVv+tyBlRqNaTJ5F>Z*~^Hzb5Y zkB0YgLeg2eh*f8LCrB95Aa1o3M=N)5GH5#oWs#9+Bvgi=#7gl!V-PEC1HJYKMzs`; z*cHfkVRl%26)X>hFSF8*`1 z!LD7I$LLguA?Oyx8VHSIaPb6{Kmv(v!_a~x#p6%I;NSsnNl5#k%!dlh1x#>6oI=LB zBpov|nyfDKx|7{wNiG)Pq1ozhY|AhT9(E{&j2)eU(N_u%KK}F3j|k|51}WID+anVb z6Rs|x)nHw_%0iMV;?7`ofHPP@QX>Zz&#)Ai|4ZZ|6xE0^C<6^PL_nl|u3+I0$hk=> zrIP9^QzfFcnT@9*C(uXSRcJ-b$D1iVW4c0=B6Jm%@1d|(cv zb8`w;>u7>Tsz4L(*5`pe3IiQD4upQV#D-3uf z2#;NZqB)rT#F6{I?E+Q22-a=M4F~mHq=smI3hDhwsg__&IaC4S?)_y%0-jons7^?X zFyO(2IH>dk01rv<2Stu|4VXJp7fd*0zJmW7hXRWtk(9DBXV|&>v|+T&ZMCmq$`()4 zmrs%X&Q{;mKSuDC_`p4jGOcyxY$)x< zgwqGDjqgEM5iv+^w|Zw0%_3}y%fnI}PjV=IoOE>S+P!=Cxz{?CW)E8oDSb#G=dbbt_zSB*Oubas7zZH#z_T=t+|B2+tbSU8|DCFL#Pqy$4E zKtjE>bHV{Npd{srrTZ4sLP7{cZB0$hwG`E`+h4vX-uUAdKqbPxo>hwdvi|FnewzcZ z7-NZ!JY*g;@_VwqxMj0Cbqrf7DLGz`<7aM!pSq2|t!B5zoaViU^Ic>e=gK#n1}o`K zvow#$))f~NvtMKFXz6Itw{#cY5b7ncrI$VFhjR(yj0)%ju%IK^=k-FuFZEg1&CH;7 zKjbz6EOL;NOSSkiH}iy8USr&Ayu~tqwU85hHv}N+JfQ&ySHSFrh5}FLG({-y69S!0 z3&Zi#6qHhG+(gUgOkG?=>~nA?y20tva1rYd$YHE$3)62&TAoyVG&lAVX3<8sbh^!5 zjGEV0Gv?#xYV)O9JnhB`)@k8?L*qhZpOM;>LoQ5AN+oG#F6E8JM{p7QY@{^Ws~50p zgMEYmK{xRQ$*%%OGEhs5<3l1M7;k5T*x}8-&R3rBxQu--i@SH4=Jc~T9k@by!;pqlKj>N-9$b$}fku45Rb;Mx_YvaAsXYNdhdr+r){U(Gb~+@(?3ZP^iOh$$Jc*QDb=FJhawSc`$_g7 z{7LoL3fcs$^vV>!WVeuP8PhZlR24ja@$H_r_Io4oT2E?+QORG*>H;1e_3Rl!U9eyy zT}14}iJl=liW*uFI$?6EVB8EsKaZn1+4@|3C0>YXd2p&|d7Foe$j!|) z$|6aR$E2zz{!K1?^qubP{_yE-9Tp$3z8~4iwwtn43^K zKuDi}$PApDASlU)v$NDBh;R}Pp^bQd^m`5rBrWM^^PP)Q5TzZ)TNv*Nwygn)unfx%D@eJ-br z(|JMc!tWr*jOQTTKhRnb|F|&mC=q#i4`A{Tm{gR9ArNdYyRHO;33S^HKR{qQsj;t^@nyo|l_aW@b%=C~%X-};LSpF5%H%%}BuN?%l%bI_%N4H2Z5E>``5p{tlKk2jifoZ!oy zxu9?lj#7D)6Yzc(AUuJJ8bPVmP_TIa?b~_GX%nWw2iW+b&S`Wm-EoiQv(DU3Iq1t-Q%VSxBm1^`}It2PM!#3=j@jc zMlv#%(SDLWsk(*(2NN6_u$Yj<`yw>5W>+U#pm~G7mgEcSbf)f@g zOu?@c`FuTN-2~b9S7m*rDFv*?ts_tI733Ei|5~8hlR>hRj4XT5$0k9XM9XCKV zT<31o6xQ5q{FAfS@c?!k5bb30lTCHDoH`r}lKFaEH2lMLeJs_#Gw$!c=-bkfpm#*; zw3rl<7f5Z$dyBGs>i6y**ts!Kp;enc&hWV>vQR86=zd14@xi3CKzv3eepu@V$LYq! zb-1*BObulp#?!wPU(BAwMi%_|Y{g?q!Aq|tY(8!jfot*EAS9m4xgS3xrG3KEJ;N*}?=$0j5f6h6&S_fCD=Z71zr0WJe}S@YN@=LWscB`; zHnu#z9{8pNo}K@pe(%A2BUx%an1g`WbIPyzu05IWR5DyjHapbzQ|&=$s^5E-xO&Pn zC5Kn#R!=SP2F)k>B<4(+?|Hey%zdU1yU_uU-`ui&^_$lay*hm>r}8+a1*xhY$llo@ zS>j;C2DgBewbYxxDZO^aVSGZh&)OoUgoiCQ!%qTxx>Vc4;UVK>?Ud*Dpn1J-K5yt( zvx{x_&PP%w?0la{#HfacN<60skCBX}2%m2`|9lTo(dE;V!Fc2SXG|7&yzp-$nC>@b@{5?LzasSq#zE*li<#bEv9V}>>#j( zy7L(7H=uOf6JERSQ34JFf8z9aW+16QjJriU?~{C_{D$a8nYO8LDG!qB6vkZ0bozW+;6Vy&mK zpl1@rr~J#cS^Cx1{GXNN?^~RFQuuz@rJ$G0rUamY=Ph@LczNuD1%PTjd^*x{du0ZKHN^e)j3r6x3Qz zTptOU(#}nl{>CUaa*fCA15ekY%;(24!WHUu4Z3mas`u12)2>B0XadqcqWR|kfw{*n zrCrejusVv^NTUyj+ z9k0Goo8{Cu`{4z65xz{#KB4uziiuXaP))xl>Y8fvM1Q?dzi0UlhYv+M=$tsj{EKI2 z1{WdmMkl!zSd4jwhHwYXAc9VwI{Ofv=Jv&mKU6X{Hzn!1LuvGj&|a(TmSxgc@bx!BldsJ`?>vMeCEI4-YSG@&#-+9|KSH``#^XQO!Q9 z`e~B{0#B2*%NQq?Ox6QQz&ci=8hwvD|+D`(ryIbrUz0CU;+^ zz{oWU?qeA$6b&vhe)JLM{}INmCPIE4IebWkf*S-=;aiXGU5@J;{wha<7d! zptp(yaHZZcvO``=D#p}l58OCzAd<-8Qq6l_SE-Wm*V`aM^EU|rE-zEoTI_uM1 zDPE~;)f|&&rk_J@nuMBJ96tVmqpf)$JFD`Jr&wZIep=3FkwZi>ZZnGa)$I-klgW2o zCEj2Wu^Br&;9VLQ9bWGuvZ?ixR_UYW*Zm(}Qmu{5sp@*Xp5R-)xJFDLeWtV}MtLaI zj!u{Rufvg{AU>_wRR4<>vi#RXTRez9I^2P-`czG()#@h%aty?lWQPy@eQ0iDGm9Dv z0Otv;^1vzFHuQa_r>9B9BKi+PD2=_y=oH!K*xkVR#Z=m&xf?%fU%U44sQ9@|uS;2+dTXizb{68yv%>G*DCIM`JyXf^9i-zX{)+Z5x9v`)5;mc#D1WT2svGYc z|9j{CYMh7>L_v>*12`sDOPA}X3ZW1owQ+%78dW`T4j}EPo4U!Y1t0>vM0qJpsBvk% zJ=~2L@?1~qC7a|+wln2PS4|FAd(JZ}2<8w)8$!qX=mtN9UAulR&-RHe@LiV=A}6kA z9xW<;01maK&|&ag*heztRQC@J&CW}l6z${S z)bxd30~(sd*)Jy_rg_&$%cUJ6Ff#a4-{X0qnM}__^Z{G}DCW&`pli!MT_B|$CY$GE`q!r0-U>pdvpV0W|yPNMRPl(U83 z_}Thu_vmpuDOKC$=k(50hiBjXzL8k+R$Dife$w&E3yFEUp!rkIFD2;SM7Hia`rb%| z?IZcFBab%&9+J_|&%bzI=~pdu&`Ol=Twro;v?x>Lxdt6*XPz#hkkOJo`VLSL7cz)o2-K8Nk(0Dg{}ZMuY18*RH=MmO4~J?Qm=#D3 z3tpVOek2CrbP&TM^oGF1q+ht~RR*tgi7$vVZCyh_aZx%(D42G&X{$s|s#QLgWKlcl zyBXu9hrK(O49q;Xq`kEjhVO+w#2J1%He68+pBilO!Hx&G@^0+0OqS~6)l6j%|{0(dcwO3%xajUEyQ9?3^s_z=U4vjkSA+n zbCG1P19zy>W(W>(uNq?*ePHYHJ}2kZ#dN*^WA46Z0_1186{)NioZejPWYwqtK)!Z# zQ=*rJ>?PGI1>iqnuCLbAyjXC^*t0$ ziq2-AUY3AdI{lWfx?{V`l_;?K(>v2p{b{*;nIh4_d6DwfeIeFcXpV9;o%^{tt4I>$=~iC=_zg!*-D?T`fX+O#q<7hO<#Q!WRbv^j_wubo@Okg{=4 z3t?e24ZEz_EWg2(1L-Q?Kk+|gsg(aT`_MFRC%G5k%ZU2t@c94RuOt^!lNE9(5_r|9 z%y&u$jYw+}$Ty`txrkzNIo0;;H4+vv6RtrcvLr*{{jtsQOE*}2u3z%8kflEM*9=-u zvIFqEa1jYU7Rcn~rK2>Lqvlm}Q({q4#VS})o0r);!xXtsiM{@p?4jG2sk-Mq<^@8jnVy(M8bbG42baW|(qLGbbtbrQsIQ#)=#Hy)%qOkRFrW2ibiR5DT0_y240EGZYazP_h-b!#Q{{0Jd8D9~e;?mlp zQw-jMWIGynw_|5kid9Oye5vd9ab_l_Z!;^bCEOh;_`(XxxQSrTjIz2cC1Gz?GCvjG zK%NGf_aiO5@SJKG{KSxvN(bS9l#pPXYsgg~w`-`X1D(H0O=c%1m=SEv%4&T)7Qh@< zp5hFAC1k=-O-rf1ZbNu%p;1%bZ8Us-fBBJ5k}$W40rJ`LQFu#)HZUkpF1sPXZl(<{ zdgPAHtrz$qm`}vPIG&`YqWXN}sAQ4eiJ%dhU0&}mYp3brb!f26pp3raiZbkYkr73k z@r?I~i=CYvmcrhd=9U(z*rtK()LHtIbqJn>c``bhCAx z>1nOf1zk%^OKf}a=xC;RRr%npt#|bG?tCOeAa6}pqtDWJCjswLwn~nR;z76_QyZIX z6Y*ewf2>^t*;t*(9xds|hVX)z6$(`a+>8`Lhysc)FrbTA)8c(dm34)e|B2|QWT0k2 zggR~=)BEPlnyjGJyLflhmGE$%O=<5OwcqbkYCq!BIOZP^Kx9IPb+Un-lUYUZ_H$A2 zNJ(|iIJuWc3Xq5iRQtdJL7L&5gt35lh&=U`ip%dkfsFoK{v+Jed(h(2@k57#-%LVX zJR-ebkJ5#Dyma0~qDuzU@?>Ofv8zMUoOE~DgcbsOypW*a zZz$wYs<27XiDa~M5z@%k@SKQiM zeFx**Tu;13G}x>D68oBbeq_d?GK=>GeBnCe&HSy{HUZH?h*TkJqudopxZj3cF+>N! z^|uNb6aA=B4=q>l9z1vzB0V5UBv}GFaI6*IT^#_=j*~fzyLyI((m0s|a5XQnOQcdj z;M)(ZR%|?Y*c~@R00$vpHLHu2?c8m^s7l?r*hdTDM8~%d5(Cg^@Mc1#Ot5WI^VaUo zn1e?ft~m(Yackf1P~ykTlk|w^M;^O^CL`16!m0UFzun6J^EB_zp(x}0AuO2vpTBsL zsF~kKZozt;?89Hou)*L|QB)Z5TPz>uOfcpwW zYrI37DbyGsp+8NGBBB$pT?J5Tl0&~mZgqX+z?SO$$o$zEl4~1$oQZfhoSI05c-?Ag zRC%HO=knCAUHnV7!bXpuN_@b2eM)^FWHh(ey5tj(48WfiEeT!k*y0-81J<-s7>^ei z5cv7d<<~Gm2U;bY@}?0U0OH-gT|+9AHzqVR7vw#dEP&c$2<#-CZ(tuqsp8~RWR!(? z-lH`VI87jPW24c0Gf5{6@d%~B(s390yz^wx{X316}7 z!s8RYU4J@NTKK)@M2?r7rI%N)7gl{AsAf;ut;#~9jXKJXQ&NMC#wXHt@2ZhDry@Zp z{)74K^109V-%HRa3EEpduP}=jjDBMk+jJaL5<}q@V$wk9JS)DC(TO0>30K#v8J*zH zTR&{p2kgHKDZ)DpARvW9(4e6$(>o;ilM;DlUE5}yKo{rPA4Xp0lQ2Pl^=b2LQs6rr zsg~=Jm_3cuP18*zIR;h`$A*-t>EU0}W$O((>r97rH5QhyRVXs%2)IW1JE!iD(Yr6k zO+G5NvyI`E!z*300CB(87o$5Le-TKNz@jpVb!p+ug%1*<5oar29wx^E@L)Q(9vRl9 z7{4I3nqrQk7bq}s^k%t*E-ZWHmb4*-fl30Bh`!&Z{NamArp0$YI{p zE5~#T;({XEO>$2z#4u=>MetgJ*??Br9pc!XIA+%n9-zHym)FmQkW0W%wbu0Ska^e$zD8SCU&g z!oV!X@Zjn_RvI5n%v{mGpG0y#jF3VVkJ8-5Si@X^Ow_z8e&I7~cF8amdp$hKix3@3 z%18Fj*xayP=nd~akk$S3&=p=H(T^p9`PG7F85_NbUarx-B?gWYY2zNPe~Z3N8Stnv zsJ7>*1+AGzmmblq{;iK2+1hzkGh*VGi)z@jL!Ii|;wLa;pAwL)kR#~t$u*6hvfKp^ zw>(HiJIM$@Py*MCo){E~q#!xSV5t7pGGWOuYj6=sN#Ee&@D}CWR@f=}^Kb4|QVmPS zEJhRnB5sk}3t8PEvkDyLZbCyOsi83c-;P|v+tB2|EHR5&P;Z+jUV+mSa0V+vd6inW4Gt5OOUty5;=r7hb27L7->%y`_s+)aPbK{(-< z>+!bMJpB`Jw>@&&6V979W^ACV%RByvEK5*8!1o_c&>&`}b_|0IRN%)(d}w-V>emX_ z{Pm@kUfV;Wq9(alyw^$TjSyh;zP0cgoHH+X5>pFRJXC0cCXf}aktM9{Y_Q{$5gyD5`}YM1dz zmSW7$2fmjTA6Kk;d;|IhCnqOlRkH211HA_0lcwZ%ZsMrxcboE=q9Bg`O#O_8YhiPG z34dOlix&R8M0>Q7?e@jPLe0dzRy*9ke&4IoEx^LBEE)B8S!(x^l+=D<6ell79xrw2 z+p!y5x)c)?b(U}jZ*5FV`T+R^Td0XKSfi*BT!vJxMh@q`L`N$%L^gxqzH{de7eTv> ziPz$qs=J1&dmw!i_XpgJ!Nj12m0|4U?^^+%_ORHj{M_Z;bBp(kAn643sB5=e${p$& z35XwCEel}yi=h;HZUFA|8f|Z#4?1q%?#X*9HgACP&IDr%3c2akzcWsM5qSeTJ+a&Q zvs$Aadz8%ijP&&T@(FEYak0EdLbrab_bzSDS!Nq@(RkE|$&bvPzHLrfC$Np|6x*)u z%0hQZ4d}tbc`q3o14oGv7wESt{D$srG&RV|^?d)bZLENSF{9&e)v@1I9mFkq0WtZS zJM!&wHRo-KIwucF;=&P9*R}7vbbL&VRj%Q>$HO~!W|XC>AD=^_x{8X=`o+|=G`073 z->Fl49S6OGl*}nC(b*Fka_Sx_6xGL|l8)Y5GFnFzl;DA4W{xA$u zAA2z*jnH76UwPGNeP3NIc$8imzN=+IW$p%}IrEjjB~4d1e2n=5bjrj@gKyv@6HU!1 z%VP_})2F?9XMAX)bkVVilqar2ID>Rx{0y(`o^l#Fns_fmj#aW>M#R4>qE0Rj(n@KMX}S@4 zolk9H?Y>#gD$i^*EyRKlhOyA_s`=Dd;Efj7zAZ?)t0^msh-Me^uPX4Jacg?Tdb{04 z?@gOBzV5O|OOfIfp0I5p^~Qolk>L6MGLqE)ZhT?wrlG09+i9ktZj{tml%2ytn=_ba zRnyW-_5D?chgWP2PuIzDwx|C2e5peC^kfogZg?$4I$1vNBBdh)S+J;up$!sRCU^s2 z5i&J0R=n^G$NNb~(Z*rvX8X7}$Ah7MZLbTJqZy8tN_~x+BiQHk9!ccBsuEWqXv#oF z=6Ee+pp98AOtf)o*)2=|903{eiQ0&a@UF248fnYY3VCBSW5uh6o&~hN8y*k22#?kl zjkbrogil;F?x#|kcmI6#=g}K=Y8n4-ww@eVP={doTT#3duNr!Iw5irp-{n=a*1XE% z!$om{;Y1Mii>_bt4piTDIy&YPvpg8ZRalR_k^1_^pC!xj0vXwtR={K$P-&dWL@meS z^CXF7Bg;^v-ZetmP0vaWF9@QW!6sN`#_w=64UzJ?#Vp`w56?2Z=ccB@-fPexb9C>^qN%rJGld5!rvW)JF}NoK5HQG#l(HjC79#`s@9BvEzNxi=)m|PoDhi zl~R^j<9&G|e1xVdCxQe%oRmhI%p?8(tjZFm%9?L+Mi|H|6fX#w5ZMYbE%HcUXaOzd zE9?A()E`U!&F@|<9&%1CHGlT+wfv;lc8zuqn)KbPJoV$Lzrb_Z1Yu||$Q)0RJ~S&J zCGT8$#YEouPignN%oZQAc&b@odK?Q?2eS0z5$~Cf>vGkHSdV`66tq;z4-RU<8>oi!3P1s zCgUna!D%(&eEM_e3-#IdzeWEprof$aF=_(C=at_(1iPL$Ta$gYdiUR}R`#ffk&)?a zues$AA4Kod=Pxi{(sHNr+@vQiv(gi;epQZ?2SH~fdXBYRrJKvsxKIYP$uf$oavPaW z`oM8hB^Uh}9U96#MONyhK!TaQz>`mqg-^r3&|zZ2)yzzLoPPV*nP{`Z6I#~{#KxmJJvPMt z-RvFKWGOqQ4l79wfk%xx7XV*+cqew6Nq&~LaoR7DU`xKg|MyYk~#yZe>fY)yASJ%{kw?dBab}ogubnf zO2>Z^6S=>G`#kB{a49djJwPjm#$w;1Vo7BJQ9t84i;o35ZFpHkBTGPd!9|w69@oTB zfA9hWx*DQKcOEBcd7*32$?0y~}L1uw=Q`N80-WeV$ZLN!5`?37W-m-vgSwj4}87AIdc zBU{MvBnpH#X_qCw-st@*&Y>3O(3rFydO9(r`{(k%op4qn(MvgHHe1keR*FWy3~>{x z2*M$|zLZbG^iN&@1RkU9|cR5H0{d7F}s`X=DCa;=&Ld%%Lv03+>cg)Rd zN1o3QnU_5fws`h$m^-{r^)9H(%LH3B=_uPGB`N7rIj~d_XU?E}BA<>NO(yU(8m5Udp%-QlH6OHW5N5ORT_#b?`~-muE75px)e)sMef#x-)xHW zMbXgmh2~9ZcKx!XqNi^rksj;y{y+P;njwl^Ug`$q&rkck&#w1%{ru`@-uS=E;BEC@ ziK~8~*?XA19#AVz5%%nd#KgozM7(}}~r9h|j-;bVTM{ZoNKCV|wf~K*h<>sX#^z9gP5Hi)=J+%54G^=vhH4BTx`@8kN zy!oKFUw&kUY1QpD0#_atZErI!M=@$+BVHqcQ-fWE@lSu1SMUiTe(}+*SlRK`sgL&e zMu~0SSlD7wGWMebBoesryP>LQCXG&V#ZSOJLE-taq2WRq(-&QwCIUVkb(6nXM!*tH z|6M6zMe$XuVOH1k=g*x^3Vg(oo0OOS;UZ0qp#KrpK;UU*$dDy9^D8uZFz^BmylfzA zKKVjiO&O2N9eK*I7>cq!|oHNNHeXW|n&AtIE3oSX!XG*-m=ep%mwsms-t&AsGmCqg-o$~3hr$)mbrW! zO_l5%6tWfR)SJ$X6BW(g0jH&;=UXEI_@L6mm=}ow{JLX z?_rU62}~ULqdXdNE0KD00&3c?^uveE<_9PTX?FRaH?owfYASnfH)ptrmRpDR=k|@S z$G)}*Y*Lk2a8ce|Knpy?j;CNab*vn_k`3w z21m$1sR2L2H(10V!(bLOasQtaFa(VVvE2NFoL^EfvWR{ri=3OIqT)8CA3-v+Um+XNGce^7$X8e& z+KaywM6xYp5R$Br<5+5H(rUB=h@XLLW%LEfO?o3AjjmfM62aR)Kobe3r(@7O+ireA z|1XL}wACSo2>Jtb0=Nwx5}A#Iz)3C*wpBNYYx2@5mVA1!Hu&6WT%AYgk6(#zfcujY zUxq&NIez5>@5`-bnU7cb2Ar^)?B>_m*Zm=tJ-+#An!^=cn_&gY6yw~Ef@-U>Mctz2 zEe=vC_1tTzs|#iT0v$Q`&U&P@l7jd66t1)IUpRH8^1k!#=Wp&DCq2};vY*-i@L=f+ z11eeiyS)_SC&mJ^^dUz%+v0Ab7DiA?667HQRSy0qjR$u7Zf%^+KdJdoB$JJk{h15T zcqc`7FiXg4Mh_E(+-AvCDC8N2STHYYfbu6p7Ln{?=u7 zQ;j@Rob2sK)5XIG0WNaogu4-~8~gcdc{7hKo72nxT2b zwgN9FjgAp%56rE6n?Z~ZR*iOwL>v_T|1#M7Tr{ssJ9Mi;r&h)?n>eo7+KOw&PR2t2 zibv({bDOv~H_x;mOq$%^X~O^hM^bzB=ZI*A_A^1gF$3zPpizJ9bzP__?c*>C*X}pS zNA`(4|M5D4ErCu$d*S~Znta>!=)fTOKHLlg6B8+|6%MS_?Cj0jf9@so6 z5-Z(T$45t1!wTG2ZHQmsbweYi;E~^mGkrt=^Ee1Z?J>daXIAm&8;c}M!et2*>9T}n zY&noXfz#RAXnY*$0|UEkz8)r@g8pcG)Jk}bmKGy z$-n3?l8tocRag=ZhJJ*l6H!rSIcF*Gcrwe%j5w>@x|&9hh=+pZ`n%zvbDocK--UZo zsbx>QB_Xim4jtD#ocQo)VnRUGp*zbNyl3pr2ftlO4=K{p?iG;pE%b>IiJs?vodGT+#7oSJRk7XJ;qhoFEcGH3RzM&^=69xfW#GSPH3mgoVWr zI@FD(py7fcB)#yvtb1%>BCCbP!B>IMho=@_0shZ{fa5P-%v+t0 z7zRk}q3<5HNEV*AhtUI{$%Q&1G;wwcqyrGx$o{_!vY{ZzOXcD+GJN{wY2{8 zTJghw`)&birCCBk!r;OTaNoT*@=u6}WPo=M98B8$e*t~PkL@@lzE3R-YXh*bJL}C% zXZmV(J-oc5yyv?O;~WFU>5v(=j^({ScGsyrZR_NV8Yt#_cV#oT@gc!J@Ly5=v*gRt zF1eH$AbrPKXwX~^(bXsxMtRVONDMhAzdCMN7m{ef;(jONaz7gupRw_AD$kIPgpVNRBriyL&7a+WKIEM_X;Yeg?kQn;@JCHd=Ol7CpKl( zWc1vaNs&+qL+7p)0JC?NMR48;NSFCbb9%=gMULAxy(}*dU6E`;I^Z6%kfN6p1#*dk zU=ajy(9zX3IYVt?Wi|OR^kB>K6kuO4zD`yeM7`~HD|1DYE@oZ}c&yjz zITH#WM=6){2e(bL+dHLv8#)7%_zjBoW$Cs)z->~v%zUQ^$h}F+%YHEt$ zO+zidBgNvp|K!Htez}C)O;dofY_^NhD&B<#LZ;oypVBwY_c$jdFIxYrs=KzmVmevn zB8v`xkSr}OETCrB(Ad+msLtHENgxLls=*&VIG2;1ZIgdh(a4*4LB2*i!9tMxH%*lo zP+#TQhYrtYZ5lbuM(p+(h?su&9@2Aq@LlHLl&wA0Yb^7o(;BUx^n@eYCml;o(h^LI zHc(5G8hPj%8^^}aL_Pm_IQ-|qS2x3nX%*ua_H+r&SO%n{-@Q#OeSNkxnG|s+nIp}Y zyU)!eXkczcp-UkC`QsK`zhi&4EBKM4N5hi@aiF!6KIs(R>l$hw)w2=WQyBlfe_M^1 z_|N2!71q~JPb%E`acU?nO<}cNE7>c17a}S<4I$ZNCCNzHl#-EM%81O2kcjLeln4n)W+mhIJo;SM{m1XV z&puj}JH&*MCf_jtXZ>vbbRBwyCGA&`ORixmMkEnz3;z1cj5a-5cyR_cK;vVz6M zfd*Guh*0rUamVpNKSo3hV<`h~Q(9_%=Agc+x3UudL(W%#>>l|&dHB6v)LrB1*QbF$ zG_#M&%U_q^4%V)xto!`mr;m;J)aK;89(4ie0Q5uZ0q|g)x&QbBNt~KCqHT7?DO7^h z^zJUQ`muQ&XIX~3t3}-}n2v7q9KZNraW<%jQl|X^e#|P38bd>Z$gDJ3&rJr+S4VW2 zu}`*wgXq4Q0bR)_C+W6gEM&NRr}8jW?zB#3Dl$3$2wtk73#PykdZ$EbK+R$b2h?$j zP&&%$_tq4TWQVqN(@dKCOf2Rn?Tvcs!@VMxJ+pd>q*=t}bWv5Fjy-a6*yDpgQXEVbld}h9x)?Tn_RiwY6>E$ctJ_p7PEv zIv@I3^n=Yc2~F#l8l^h>MkPd=5(@C~36Nji*s0M?fD4N3&3i)OOh-Rd+Y(~;w*U}M z$N(!oWwX;XzH^Xl6kQ18(J^8OJ|lYC(vpLUZ17XFeTj_TuZ4zPjz1~=+BZFq$hKat zY`iaP&gG%oAHtX1piVq#3GyjnOW%J>u1yz5ANye-#$L#JWYf)dc&=>E5H+h<@OkYP|(bDDre(HIweaeFeSH1UY$59?$k*DOyA3mq_$Is+Prh_me4a~ zf&_#tYOGO>clPZs;mt@+8NzEG4gHRJENPw*_EL`VIPB>*2-pu@J_cp z=3VPz&+5I(#TU0wpBhR1wL^21DggQ{`)xgJzaw#rsKD>BHV-$hzYP1`UhdAt$BwgGnfWxC6L#lIyx6oay!vKj$SCCn|Em=pSK1xm2Il&=nM^d#mp`4p{%fFV!i#{&!m67T2wy*zlpTP#~I*>mRQYc^ib%3L7 zdn@ODvTHr`;idTxt^h+9P$4k%ZEr8EpEK>2Px{#{QWw!1On=~)T^xVuvloS=B+9M- zVD_Y#m836iicvip8o4>9tcu#h*G2ibX=-bUJ6XE{IQ^jhy;ivz!?eTVPga_e^Fmm3 zJ6g`_5ZSZZ<^Xa@x&}o*-j@~3n7V&zUP+*~AY&x^WZE$@wG2L$&VwXedi6@s0_!k# z(F;v2Y-vvs64=PRRd{Lk)4yMPwZ5Mdn7hpKh3u5i%~{@t^Xi`C>X&|)?b!Ewm6Rk) z@b~Ig5+CI3XLrY zY&4JjT{XJ0pPDL+dy$xw#G4c99g5Qu7-G%mD^4quFWnFc(9#Yx-2T6xZQS5c!iimR zGEu3OqPFh$C+(o%n|)8VL2j~>nAuE?4sNOQY@y?cN_o_rl`WQBTWpB0C59*o*Mw-b zz+5G6e^7ClarM~Q6_U%uY5<;wnTVD++yIlX?=(#@s!lSqUot^K`yqm;de6l zb>sMy_V32=wmdcw;$<=w1w96`Kacrz^8IX;r)9spOO9@in<$+8IKjC?KW&p%COo}l zO%nrG4%^0-SiXakMYg-Cf@H+ca0jzCXTCm&_2jfrsQ#p|?5XtL$4}nqcUauNd3Dlk zK!&Mx)S%$xaE@!OLWyl~G;nMhFRHU7YZgEG2`0l3D8kimy$d&pVo}8yd?jarWp>}eBl{bd$-;;y{7Dwl} z40Hg0KYlp26L%w?vU4$3`xUf@ZQ-`LQHk0j{1;j;d%G1~Vs`=I*eL2Y7+;=1@D{0) zU<1drv^^h>AC5e`F7GawCQ$2mLzyLMz(|#h7$h+TTs5V;+-@`K^)hMf+dZDpCv!Tu z(MI-k%fWoh=XVck$MIEq3bRJ0dTs^6uFx;TDNfTnKCbnTOt{@NPW~-eFEuIoV%3J2 z)QiYzQk?U<6T#p%_i8bmeeSvjyz zfoygt-5-B&{@kt9*RJf%t_q!kh5?j(F|VgThLC5|d%w-R@qKx6@K^&?#Sfx<=Bml2 z(lB(hh=@-sse%aVq*0WnQB)?XtsT?}XLDpt#$NIo66M(8#*s77T(%HM#?ijBqd_~^mBErj$siARETrsASi{FM{7 zpJpUwJCpz9Y1_GSdwli95&wHY)Fe<<8Z z!~g&81=G9j0Gm5H?%oLCVzB$TYx{Yg4w>)tlg5fO#UFQ>Tya_zsNYeo725zH+!Y@h z0VxOzjc-kAj(zInjrxbE3n*?}tf6Hh(IWz{`HZZsqsJ;hW0{`J?M0?U>g0MrUFW2{ z`?pJSi%FUvrazayFU)K8D|mC1^!1}x0vdTJv6@tTFEPSMBX%EU9Q&ZZqBW{OqNWk) zvI4tLY~duTH*@TvzyC+btPM>p`;IQi1T(0fuDNWc|30Yd?SR6aPru1y&rmb63T+|Q z)*+vkOe#;g6fP>ow8E+?Rx>SS0_6X9vs;B=2Gg){Hh z*8Ec^39e^Wf2hERnwRbA2{DdC{TGLs4{XT)Ha>2uGaSOlXj2h10seu9t0pC+{tP;} z8b3C+Dl9K9-mzMwd<(4G8BuM+1TFG17ln)=mJK`cUJu`Dz%KYTXyG>`& z5@{jZ&0gcxd#54wqm6^BZ~yWg+gBJ7zdM*=&lRFf$m0Iy@q1UO4ZHyVpjM=!Hh_^E;# zM`e5#&hzbn8os5oGjXmo>7+u=VegN*+rJ-sI;yi>ReM+AsRDld0Fcwc4B!|ZJ!)|% zJ&Bu}kl>N!N)4>>#jC!g{eB;SIibZZ8fZ(y(Md#!l$EU@=g9F*K(hWMpuYWjNlW;@ zBUmM;;|&y%xuG2|DU!IovFPis#kb;}ORK`s2c{MS_UShudG%zW)vQ;N zSwg2iR+ji5h+e9HBBuJ?Q}b7DxFo6l4(fb?ZSk;F^q_sAI>97yt1ti)jhk$q6Xl6* zfe!lfEk6R>2KM7ep!8^o1TRLSrPqMvl~2oPr&D_V6buPFG=Paaznk)a{q`H|o=73- z@@@xIL}XB*Q~B3lC4lP@(F0%1IhHa&W{_asKX$P)-bm2Fe+PQ8OM=wM+FP)v{tS|T zWcEfg@7jg-8`-VLgRxIDe^A-BsW*Mtgs$n7;OQM;TK0YFWec?dHoy{!TdAJOFD^F0iQhkKe;w@2(>}J3WBHx3U-;{jL#X<(=ZLaUrX8z01HQtY@=n! zT|>0$nDq2jiyO}_f_^tO-P7!fPv^U@*`L2ktEPVJ@=yQN(>pU4AF(_q>SV*JTxsM0 z^?JbOFjUDsg?P99`pMq2rTxQRLzmHsUgs%cl|(zpze`i%h?;4-&D_S}1E>bT3>${f z)vN0S`U`>kC$~0XB6)5#l50xK7<#V7> zViF0RuJ|vM08sP7X_53O54MPY3wK)8N3b6*uuUwZw|h)lXyYh!-{}Cy(StX(e@5?n z^_wIhGq5}8rN}LVKXIzf+nxGaT*+R1Qn&2?PdgP=FT9QCn$%EX*jVka8-_9-^u#bM z9~$eA4&lfHpAZ%Ii%+SoV**EC+10t;>|XqA9msNLJF$DG_Q=)Pu-KNGv9Yoa>OUF6 z@Se|ATDOPc0`8V|i6A3r@5>{OWh_~ z(YkXP*$ORfZRYTFto86lD+DnN0#!v0Axr4M|J&rShM$4F@rMnFMMx_V-%wbylJt2l@zUMDa!~m1i~bWs{#CE{51*9 zD;b#=sItd|1W-aJ_Dx0Fumo`#xwQLjhlYaalXabPZv|Sf+9#!o+CXN9%{-0|!d`dc zT4h|g8btv~4q;?sz^tMxEH1>XmgW+Yjlfl%F(rClT~~eyHNik77Znyf_-1SXy1hB? zi|j}ZLx21PT8c(6cbh-Ml%T>ULfpW3{YH~j1^j}Nl~!6dDM!$c$Z^XrA7(}KS+us= zc@Z)~ON1J=tdK=Qac0={6T`s&?Xt zku;k*K&w`L`hmyYY8iv2OM?n@b6X+88H*r&CYqoPIPS%)7T1UZR|AqD z4OuXJI)r{&%JFo@;xirj;~81IPp}t$NyHY7gr}$XGx;>H z5@pQs=?BjHbZ#}BwST{|-^TdnZ07cjq~VMtryULNW7F>BUD-M=5s05;CP8c+kWYwpfj{5bm;k%gVB4wjam>Nb`O3A%vb_(AA(md4ku z&pJn5L3-wQGzX*#`iY(Jd3D^~OExBvmp@gRI31PmKX4?mp`ihc3fwGiXwVE^jSWaN zn#;N$*2ukuV&kzfhrLt7U8-*sBdRgfn{qm|xl;wB4`;DLdG*-6zAoN0=34hTEjAX) z3JpW!D)yxppV##>yZzDs7tO(8G|4r~NBhE;AY*e3(ohExJ}FS|Z%(!MzkiR)1`HlY z%8!zx?+zVmq1H_ObJD4Fx-Xxtt(b6iDeUD`^oNrbmYNR;@j}cZNIYa!AvE+J%-ygV z5nT?i)csBIe{VK+gew^KrB&>(mXHvHC!tYZl>VgtpN!`&GD6G;nZM3^kLLntI{WN( zTKOB+%CG9@jKodebT~%t)Nm!1PV5Vnidf61;om}3+v!TTiTktedip`t>!w%YbiH1r zj8%9Xbv`U`K$E%X`kdO}B}P@7_BpZX&zjn*-xT5%wvs4o#r0iwrs583gqm7kKdL7L z4(CQu43;`R9iqeGLzNo^$nQn+s6TLjCJmTDlo+TK=3niEC!n@A0G+qtxq#|H8=iUZ zpIoX$Oi~pgP)v``sXBXDECX8N5mH>E%Omo$(0 zd`k~QVRWGU=MgwESV9q?Hixfd(s(w%H(5WIPX}Zc!kY7Yr=UP;j^-p{`}OrZkaf(U zitU*y9qSS5cKpjO=Rw8o0WShbr}`-m%vdgVIy6E!wNZ4j;1AY9Mpc+$P(Zhzn(&ar zZsnbNNJQkWnu!D}G)5`X&Z%$CXV|YkO?&>weHlJPc-E8-&xQNfe@s2%$DkT%pz7*6 zkJ&MP1rB(e~B& z6Yx6RJCM-`^Cy%u5LW>sJ(&7y^eH0aZ$QBDcYOnkEgJciBhq$Z72&4@*#wXH49`%5^o2DyV3A=p3!ooq@6 zmO;gZ7G6A&tGLk^nV}4WWtShkcSymsPljfg=s}IWf_2UYW)Bn-Xe`+aJ0{vao+}wZ zx`QZG7(OpmU@iI*e!%O;N7xm9y*pt*xI8crvE8+Gbfn6<+kV-KHoi4K8@3~X*P+@L zyFamTu06*Jh48@1JON(bMl=AN`ChZwA&<*BGDGYNmQa6S^D>Cf5qi+R0?*Z|f9)%s z-&nYI$oq`PYSkh?`qfD2zN2?h8|Q^6O&ql;8GuUdl7oYhQdg<4=!=b5)4#3rZFO2K zQQXh|cC*chaeA1~{Zs8%coU8hrMe09p;4pazIy@vBza@0J|-((Ml<UK{y0X##Nr!T>pkDd3)6LHimAgQF_U-1XAK%% z6`+PC-9mMlS^cHybIF_1zK z4tqludL;BVH#`FqV@jTJN&mR;}YJ;u}Lk_23sgg6VB?k>QkQ1?-& zZO~DQK;fd*dDxpZGQ05~BQ8#06+C8JQ=cHVgDM>FL}|+cbC>wH@=dxoBTG z_dYOC`k>DWQ;X+H*quD0_5wO=gcN3g56ixcs$?pnG?=+aC-BGFLYqk=YHXb4z>O`T zvp199pGQ})%x(s<>kKI8*qgzv5j>OV+Oc>ot_X=!AH5@kdqrmW%-3JgmQ9gv`K7z% zpA{uepCMS8E2iS&R^07gP+1Bk%=qXg^*E513%n7wChVa$Od8!1!b+{In=J_RBqLqO zj_FcL6?1KPNj1D*(DH70-|DZo1@s_X1&pvxWKm?58}HOGtlc5 zZ)$4CDaYe5CR#Sqm`)KTGeN}0@aZ@yJJ>!@XSMY~3UQ-*-_lSc1t+b=dyF1Ai4dRPhDl4lVFf`o&q&#wBNT)J00`!gi+4o@hs!khva<81w!88QFqVFSe=5r z*cRGPYRSuA^r)s+|3);rJ=x80@t6>R(^=x1c56Hi8hqO9NIO@2{S)*EgBjM8$zR$f z+xyNdh4Xb=y^zq5#FCr`qVIA-v%>33EO*WS@V)65wA^mzKEAZydfiKe)c$G5pZGU5 zhRh%C=T%|2?t(w6?8*LLrq{bBO%$Wj>BJ;C?1Wiv@doQm{-xLskK~?fX_loi45W__ z!dYzu2aMed^<k=oKg!;+72B$ykd70pru*M84n@<+&(M>zQed3?Y z;7|O-Z5A}QWqKPT8;{LZJ=GF(u(rA37iae0WR~t)u`#K<9m_XyQh_|hS~+yPO6(c) z+N(D_Z+#Z!EB`{hYwnoU|9&#M9p>Pz^o$CW}UoL>}O`4gR4_?P$ZnRlCN!S!{P zTS#Vi`Cq%flG&Wee}!*Qa*Hcn_R%f6W9!=4bt6}%{2p#$GhD9g+8EGzM2)+L@Gm

Bx26ljRP2xP3grNutz&Qm@{zSuXR){bVR! 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 LPA*Hv(EI-Y0h+@N literal 210056 zcmc$`bzD|!yDo~;X?Gxsga}B3C?QBpm5`Q3QYEAtX|Pc#eJPbvq*Fp^FzJ#MDW$tR z_cfgBTYInbt@WMt+rM-6cK#t#;C;vYjQhFotDf=rioE2Wos>Jt$jJ6cT^3U!Bm0wy zjBJbD_O1ArB^}8r{I&IljHDRZ2Jzpk!ngo3vJ+%dVi#1L!bUqh-D>C7H>OrxZ;djA z((3b(uFxNA8vK%BC$XFn*rVQe|LMI7x==-r@S35E9t=^+N@6Nmiqw+g9_9gM{`)Cu zi%a<0G~c(Dc!(5xj*jY7`WKcuE!pJja^}BnSy^%sp-EHM30#Rt8>lq$;r;{HLPqws zBt%}#@%NWxmKOI9{r>8M(*MaXYSj3z#W2bQ56;wzX6Nl8BfEB|%`#u^?9G}i;{RT0 zx%GV+@5rC4=I_$zz(VGf%#XFjr4=n#4D*qZeb{KPXJcYWkP0}ZnSSFzlLZ;sZ8PKU zFOQ%2`MoE8e^NAiwqnQc&vi}s)4zSe@~^+h+380m5EY{5an-}aL-5w<^QJN41zLFV zYb`;WHg7F6$_!ta!v$+~Xg(;I{Iu&OxBf#?KtPJ+7W`b!_yKvcoK$wrtjkhTQi;lO ze&&3kx5nEOK7G2_l&aG<+{|h)H{3+YZy82hFBuvCpTrA?yUYzI=oY#pYQ9U<%(hHn zRZZ0Bs5eZ&?>BM$`;YM#^i_tioj!eqlan)Vrsh03Ik{^8Oc0y;aM8-R@;lR}!-o$? zr)Xsh3Q4-T6_ofKdz0rd!YUwewWriyeeB1(zJY-_ayGTV7d>rO#i}dwV}0Y}sm&=` zBW2I%)hD7hVf{k4|N8Ux@gJWa?BaDC*A^ESXW`~ntPbT@pXe-nXEz`(RqwezLpwM4 z?NY7qLYREGU<}=*hl6*v(LAj-QVwEO4SfFGsa#;|KAPkfJx`s4goKxZ4v8+agReh( z?MuRqqC8{pXy>k7j8aDHjLop$`^;%YihJ7ix3Np*1s6tDP2M-=l zp1+mcUmKZrO73O+t+5|3&))QV;_c1rzHBd5zm3-Im7v40@y`!;rY9%sGHXSWRwjz| zbn^>HjgNutxczv;_qO9zSwDc2(AuG?7(2Ex@ z9zJk@&dSOPcSdGyc`kFb^Y@w`yqR>I>~`h5o3%7Cv9XC)GmQq^-&=KwNRjgM^Us_; z8x$FtmZ+K}O>%R0f9Jk(%WHtSU-Jsbl&TZey*Pk|Z9>_H(Lfx;npp{Svh6_jzy87W@49 zQQ_$_`n|QkdhLfqS@%($J0UeaHI>WSLR577=yoQu_NQv8+6i~>{vpK}dR15UufJ}8 z`uv%=?4{-9MD;XXN+GAW{nepNQq$AZtd4%ufvIJeFJI0X6~GJJ);fk;J`&8`f49{8 z$&=a0ja5nN^S1&+LPAP>kH?ub#ImrkNWFOR;>qL3icU^B(Ij71ykv@1(UOFKfPkRK zYK~D&n29I*8`UJWz8ahHb^+H8rLSMVlBDMgjoaR*goTA=nYW%%UV*_iKWg{`8Mnwt7j$T+S1f;pCJjWnKTg^_afOl`F4& zkMm2Dn$z^SL`6mSPz$P{?v;x@*6h2#?4TB~6=PFR4P+2ro&4-F_O2y$&G%1Sq2+$_ zM>~Gzow@db%zg6Xw)qLIEMuFGHY3d`0SvxW0!lPAG{jE_1_nkS;m|a$2xM^?eC@>` zr64>P*y*|9Zg}%%h=3>^-G#N41$8B*dd7k6J9d1Fl?%m>h%80RM+hY-h_0z%4+N=9 z2m7tAx>dh8E7{rE>9Ww_IM;eJGQo9We7PZVJ-V?`MR;X2bGR+rDyEB#SUjWaXDPJ! z-FI!@w@+PGmZ%UT2R5=)644si*ktZ)+xMQfvbwtI8NDPAuHOQ8pnVV5rgpbe@?r6V zgT`x%J^pIh7y;2-R|Pixj3uX~O~dMiXAj&%(T*3bSYj7=Un6Ej&fZDSJ~B(qZTn~% zxr$e#etq^bYf06qsHjkqm-o9h(V6o25L2RFv4>jrcKrBX#HPLBFR~o3hWgMi^&8q) zE8bwDrKR1nb*pJ>hM~*UR|*ujaFQ0W$S3%|{AnF9QXk`;XFqhq;W++@jPp*=|BX5H ze{$yi%lFDL3>S7)^QPcZv$lSFO7{6R6P+X%V$a+j+4SpAR)f*+=^3r&hD9%VOq5;= z^w)$Z`S|!GHNNgqOVNx`d?P`We#ud;7|+BNG7J&tBc#2uFxsR$*U?Uk^cDO4+g39^mIX|$yc8T z26}sqo0G4qCl&5=o-Sva`@B!|oRCLlFbD2y4AGz|^xPDurlzE&rRzsqGv7JS7~!U~ z^777Q469{PkmWqJk4Y+Cu9OrReodp95F{99rXY;p3JplxrN>S*c>)0Wh+x|}zX{)s3+wA|pLjdfRNXXjSYUAuQT7C6rw-oO79 z8o$mh9ZAQW~Xm%jF6iW!#(8J)0v{#Z{9n_hAbOC5QbL^T^-7f%(QPX7DV)zz#UUw2B?Kc%~r;?G0HbE`A8NgividN- zl%M$es^St>*Vby!r@ei91s$XbJ#+N9Sq-{lr2G7fScS;coSbvw5)!6AvP?`HVtw!4 zy&L%S=?SU5O>CT;Y765XeIp}mdU|>f9zGl{-dMAGkjfzi~wzf8vVBDEu z-7-5Dc@ydUZfCOgurond$Dh0P$&loS0@qVK&HVbA|JpR1w(g>&kocNXcZ?60TyyE% z9ckVElG^(qGXRZ1o%j7eme4bC5f$m_o@ z+^~+;y>TOoz*4=v23V%D*4C>@YAK0V{85t`3&uV+##WCi;$7)xcqX0V+;FKF#xXWMB__C8Xbeh5MzJ?;+&D}_ zmToKkycP> z=`9Zk3JHlTE4#e3vQpXBruF*uYoN(cTU*;Kk2M!5C0pB!!flenO-bqsq%8X(B|{@4 z)2~m+e-t=p+S=K@^W5;TXwOkR&S(BqCFAu?dHEXgGy!&YS*yai;JsAmUb#*bs(T;d zP{PgKxpOChfjD&XR8Z}zks|KB0|Sm%9jM2)kX7I|Hpz7yj$w{r)A7!_9Ns9UsuHsr zfO<^6W_cY;$9Lhv1+{FOOOJhgct{m)yq4|X?FZ{#SEUa%#DxMF1Pjcg^j^-++RyyF zw~rEoIFkTk1v3M+{6Ix~mX?;Mn3>CxwX$oD>Q_|#>gmy!@Talx;ojSF{H&1=2uYsF zx2Ok?{rvc3g4wx&dW;(56^(!o3`#wGmA2ugAQ6`W~6h zt49ve(4+$J0<}~RtD$6hd2G7H#3dz1N@+c_^2mW2+{^W;iH$?{|1VYj*OQpjbn@p% zOyWKd%no(q&Rn|zR(kp#R)?`1pkNcM5>aj3C9mzyoH@gzU$Xn1NrMCyA}~8U8+3!f z!($!!>J4!U#mRXg4ZFy~;!aCeWUtq%W7I!68TLV(L*wmVXe`rnb3xwT-ag#(X;D!p zsYN}sJy&~ZgTlkdCc}WfU*a`YM}OE5sM2Hc*EX+GtJVy0faAv1rJ1>IZ!T7JKM>KE z!mjW6tU8lFeE17YMpv!l$^0K=6oKwyPg2<|hEFjjKYdc8*#Ty9`%+knC0PqUYNaVf zOC3Xs)2_X|Tq?WcR_OYazo!aw*ijL;9B^c%Rtw$tKi-+0=NTXT_Gb9UyCh=rO%!kF z;?I07<|MCGr_h!^|1<_<{Cl$fBt4mliEdKr_GGxJp0KVL-L@h87HQO*0WM)fQ0SPjy%owd?CA}=4$zszPTt+gx_*Bs=AIc-f%htDGgJcKGY z+Yk{H)YLEWdB)%{-hP!@(7q0frizKep;x5szB1p0`^9TmaagJzd)LtCSzKJ)5l-!R zqOMq3r5`_jY}#3n>#{JO5`Ny1=$J1B>|&Laln#@V$ADFr173i)HvCIOK>9C7`OAMb z%EfKUBfVIK=&AKMQzlfV2TT2mWlU_E=kXCIMUyA)UjjknTnbo2DGtAD(N#uQ3+B+m z+~yk+-B|MPbe#~!_|=&$fCA!6eO?W$O)21%9D|fyf7Q2}k?yhhz?j|g&z?Pd$!GBr zqvFl0S0@-57%r3kSA@X+9}$A8wstajb!AJ7I-u9iJ$srwH&(iK>bOGqfWFZ5_WGq< zAO(AQ!^Xl_D7ow7LA9HI_6G)757m0lNP7r}oT6UNJ}fGk*eqt+sAsw;Rz88o%Kq26 zO!-(DY}#*4S0j+?-jLAO~> zp6n*Ean{$@Ge|KhLm9(7}in~s#0mX&S%`SD&*SXlC-N81I?pEqktRPBpc z(E5)^;(|YQ{A`=?#mk>*v~N8I(`tE=l=D1dks*p}H?;cnqtgz1vm(XNK_F zY{T^YJg40#2IlhQ=Y4ay*F)Z1MRkn{%3K1HL;a-65U#Y@^^D9+aw;lzNH1gG(=Vae z3zM66ZQ8VH=l1PaBqSd4S^TiC9{%I67~4e6>7*oo`+?I5%E^su(T%381kQL%_3O{n z@0DD|$?fghVAf`60x$WkBDU_OivM_To7t!PJD}`@yPFRsZq}E+^~9eKu^S3SnNLPgy37=|8D=w4A%Yu-{CCSWJAi-NOYG|vz1bFbaFP$Dq^IwjnBam?1$dhX z^`N4>{M+)}2xEfmT0w!(*RMgNXNvzx_eev*NRfg$brP$pWK%TKFNX44saaa4&QElu z0R@MyEWw4==b#Z z8{tK&plAAUmtAs+&3`k3>{w>d}IXS_ai_AKrYOTb4_u2kICH#?UJGMRy-*%!FE}zX(htI* zsu?-}#vM>rMT@ySyXBWoYl)&SZHa(qlORWLu@s#Q{Ly1XQM?rbZ215>x*q`VPob zyz@-|@>D4;8_xoXQ3ee-T9U?D48jO8bO6>2eZGB9*H5b_57H=fc?aclpzb@~I^M7{ zn4r?SZd2l4OH0`xg=XHYZHAaie)MR3V`HQA7#9^I4GW-Xi$HP7=*r0bWeK)+9Fx|NH5 zpgjKk|6S@(HMN4K-U6AA)uCqO_1~+(r6FJwa7@59`qQUR_M_jq3*i#X_n-}{U%U1S z^w)kM+>rnjq%6x0L4vU3`=C78qMWvab#xq8GsFN*-lm!UD{8F%eu-2|)RH8l|4 z5O86%-?2KqGWwGz?U!dw&~#H|Pfuypif$APx1?EZc%Tl4>t5dz5)$ei86l+FR@xWV zy)F-TAB+0xO(8=fx?N~>ETGLlODWh$EI@IEE^{Poc)Ubos!smQClY{fJiZ6MrTj+1 zm*-|pn0GU7I_E+WaSJ~9`dN3MBJ@wGdZtLXlTx6MuL-#C1qkMjjJpMjtl&Z*=8*(o#~hL4Gn~#Ti(>9s*`7*J9rKsR9)9u zoRo!rC%ioP=E+BUHfNI_Ge0rzdSnd&xr5p)vLqb z-gtpw5USo9CNLzZI$Yb>n)~WhDK_I%qpIhx^*olu!GfE*Hr5@9f2T<98ydR$&2#m4 zt$wBp%h`d&lvOI%It}E+duvpKfID(8c@)9qcyHEFkyB9U+4hvhVV4!Sv;D(SVN5#u z&xXpSx%K(%VipDlu>%JV031LFE^JKGE0)n!{u`JP^NWHa#CGBiw*=uJEiX(!>N)M~ z;*#h%(YbTqz7{xqbL06Fg7(4`2Pr7xzP*u@A^lHK7!R7v`tM3l5@04PECH{5w80F0 zeSIdxI)NX(?3EDJA*Py5qI{G81pR-K_M*#E+=^US=ws^~PC zq8Z-+7^Uiq+*ddSP-eBxrP2!ztsNZR(b_pWhA==c2-LCy`ukDnnoR?ub!O&0yc|-Y zfsv6kslC0OQy{ewg#`9Z2iY@NK*XSIKfD-gL0e%8W$+BCdL73O2i*=rL5GX5JfgRe z2+sCJq!u(iFgQ4NWjw!DqzDv;5FlP%xJQ-8NnCKwv2?nAxj`&FNoAU1AS;VUs=hpc z39G30+Bqz&rZ$z+el)uxX#t&0CeM1(hJ^rhwPrpn3v;oh{ju=6&fy zxb!^aOheSuW_?fW*s+8BCI5*Ne*we6NUOt5sG!!P^r1Ru{j;DpEIizyl-f}xzKH&x zfFDQZ$v3zmBx&6?!YC(OB>(kAuHToxvkqbkIV4=HPZ9_;C`EmaO%IR74<1}K%h|Ka z+}zwKj_k+~SZ)A!XY7JR{I2AEi(x?1kXI_Fr;kAoAM2?|YI$#=1!8OhyJ3FDV?-%w zr}RRy)2{~160w^AYc93WujNTc&Dy-! z<@-!}zW1EVY&?N9;mCgX4~d{Se3)V1zJ2cK-q7Glm}K_-RmW0v3x5tZuE;!_b;A@sJdB_%|iLvRWNL7u}{t@!*oMuSiGA6bA)|H}1JnP$ES z;S|gI4k*tx1?rUy>AiVt6h;y&7ncHNJ}G+7aX$1gc!AKMzKL{x{n|&GgQUu9KX`@E zCkdJe*T%lL>_Cckt`cb$ZDRzyE&t~`um3Le!yq9v4MPKic&n}=LfL~N_HfVf*qZS3 zNjGaF_Uia^OYoXDLjH>Ua(Bxv`^lddRtlx$<=-z1pcbVx^XwB~pT2|8ZqZqA6%^=4 zv1c)y)l`T=xOk#Jg4HELe~NG!W|l#o`aY_M)kG>~fC- zh#tZCCq;v@))K9q@Key2r0St;C5b}$7;ewaELt9Bld2C;5J`A(_9pAOa~f=F$xl_L zkN9Dn=ww7N5GEd1;Vh|Wec24+hc;k)bL9Fg6DGSfLJNrx_forjb3JYT{WQ_6iIGT1 zD)CTAfwoj@BZM91N3Rwx^@q;y;Q-`GAv{8C;udPh_Sf5Joc`+DtS_B+-*1;WoGYky?ke?(Uly5Ik*$-QkyU#)TdGGS`{4~!Wu z^WvOMRMeX{wXPO?U~FGPlEa7>AR`Mt`M>pr?fX1zC2M!%A6W@R0OGdRVK&5R@aJ}n zh5)`*an645DcS_9yDmPL+D&|8*YCF)n3|qefbA)GoYDVn^52nMeuI4vy=Zi=nL z%e6?hArL(LvfYWWoC*LOuFz_=elxHl!xAS~*N`PQSV9L{+lHB10!j-AeuLqkLJrP834 z(F7#B=oV;@RNhGJ0puQQ&sB!jMeO*C7w-Z-swAm#0LjW>tYQj=xVgFE zC0--!5>64Ed^J-7-wPtR6|ZTufL*_KmfEdbw{jPVuo0Q%&i*5OF0;mm4j;B}8VM6| z|8e;Y24c}5!5>~?Eh+VVz=jGFlwzJdGm!8(dL4+@1b|T%_VU#HJl~crTS|`VBRzD! zXL#5YgWO0yQp6&dOV1LZ#)rXfc2H5@hnwJRJNEA7!>2oAVq(&tUP^OTUS8e^n%4Y} zdt3LML4(WiEQVd$Gd5L-9(^5f+PUr6678W(OT=eHrG-Os3ePbs{%!@ zUR&t0LORPB!K-QjbUBQ*>dpIvu1yTR@fSIRDnARvaEhP5MQEYjPJ*;F_{N`8V;5Pn zEk;oQiKLWZ7aJY@96MWWI86NpB&2>|&flT2D>wGAF{NID^GyjJ6-^@F4Am|eX&4${ z1&lmqMn-YcYV*|a$kDj zXA^>HlaH4_bLpFNqk~@#cmSoY7!$Rk(6i*IFs_#94k!yy`-ILyS2iIbZG=BsAR>=U z3a=MJ)bynh)+7Zy0Km2p^^JPe|rm|cYEXO(vwcz_& zzYqXMG~8GN<0pCyP+Ti*t1(Pf27+#fjP_l0)eme@cAldeTDpkg&L{(`z#=+W2B(Vunzd znf_L=punOC@yIK6*YC1dGk@f~=iK>__I@BAUHdAi0;8VX!P0!?1%a@mF6UW4y2AR8 zY!ytr^X(H~heL>~6Iox>mkJqQx`^#$5ZQ86+48&H>Y|O560Y_|4SR+SHbl|W+qcPJiV&7M8V0MQo5^V<%Ac#j2r`Z5^!V*; zaumCD>pekW;GN>m5?IDi&7cuCO{v^W>$mn|ctBp89^|Y6z|sL)%`T7in)4IP3=Ch^ z*RT`}QqYIy_5Y4wk^W-@OIk)o`Ck~bmb9V&2CxT00I)SQ2CWtPn(U$nyjeOjyZ@g; zs0ye|)2j=c307&?>0|@B)6me6U<^Qjvm>lK^sB^6FEhH@QVF}L;J&Cpu^KIvsok}6 zC&|mpOJsSF9yC)5`7$EwMudid{Q9Ea5%>6~+F$2ve5)V_MMOjpV#!>Sx*iLp79e;< zLd=E0fqvpJ(W#?U-v9WYpsyl{@M;Nv2af2EY^z8>ytqS5@`F%cHFIra7>0g%(Kf@r z=XIVoz*@5)#Ie4eh=@S^!Wqx$1Ztkc)F)gGRSESeh*^s}=>Iy{&z||8KN{WUG z%L+~e^jQZX3d11@IWsfl+FqW%e%FWFr7uXG5Lz%zMiSy8lmILlhYB>I#gVUs?Ebxh zRMZNYdR?qjbQv{GH`dpTKHT2aKi_7>5B5>g*4EYzOl&ki+B&bUPN^@8w(rBR(t?>M z*Y@6`lFD_anqThv_1Df&IefsDr)Fn)09CVIoH2Z20`dI?#CZgePL~`#`=83U=7{V9;u~kwb!(*QlC)0Gce?~3OHjlp{e~>FmYM=^t}EZiZq`14*#X=y3D*8AkDqwq1$VnDhU z0v0_dBD5(55U_(lp@5X5R=kP3;nosN1W1a_qK1w8Z{Ip{Vb~n8 z=Eq$B9ntq65-$^OE-oP{Yl%R(7El7LXXpqVZRiA@zww0tY#5MDP{a6S-QU0e zVNY3gwHyIKM`zNQ!xonYB7>k4Fe?B4?(O4YaM8#g91?2&-FL~@#n1h3e_^=;8Ct@q z1R?&Nwp;bfY}$jn_r?{m$ajD_XG*a;K)fVu?m#5hn^Nwhja8eU6 zhY9HcY^zfECU6h3zzMUu=6qT)wcs?VKGjF{ITw17(?Ty+H4+7ldrA-hxTP{Ak=4md zn1@M7S8dx%RYwE@Ap{d$va`UM;JsfvbhW_XoAlPFDF|yCH5R}SF5<4~xm2kK356I* z-%F2BP_O|8LF02lTPDobq#rOR$bU{uq+zgXj}PfhYiuJU3;FL*#+b=gxYW1}@__hzRsMi#9a{p{e_5!}=RAPZjjs zrXFnFee@N7r_<+O>y`Of-&eN#iT&?Om}#VFM0Ax%S%#HIgf~`aT>4+!M7;161P^)Q zHzVBVuj0Ib6bUKLHxPhW(aH!T5Y3&(VOS*vL<2K4ugC9vMdKpWiKYxgnUw`XwIJpg zgh#?TOwoL&WN)8^TR}sl4gtQ@FdFB+Qs~8dZmer&89yU(nJY`UarLMz9S zRgwBi1q3jzkCs|S7-<&tbg*!tQyK8^ol!LprLIFw!gseEJkF2DFo8obNCOoLxpcA{0B~ zMF39oFwTk4C*U}dmc#x~1>>3huz6o#!mqwQ)&B1wx+zd*)JTnmu8!}_Ta~bHl#%8F z@=pe@OoHic>)@b@Bn)FvGV{}DWO<-F%Mb#QMRrFl_Kb0RjtzwEW{8t>AGgsCA_yPM zAUsob7N+D2Az@*{T|hl1{ZWM+kvQ_!1XUhIXN0D3ll9D2~2#xHk8LC3LIo*!E^C5ZA}t!>f}sUOY+s3g7laq zim)(`H39LV0raWg(b!=eq*2Va?5Iha^4wVPH0)ousIKQH1=iTHpy_gC(iv%LzQmn$ z__)rc`WYTcYildo!&^N`z}t5?YOx|f@gq8^AeTB9<^HpR==;{b1e_$k>m|qB09*IchZSvx=@3l4yIEH*eit2X;z+@L&{tj1ex+CGQe{T2Jvp4Wb}( zns5jNN0zRYlNgD^74WfUzZ_DCmkwej+*hbTJb*4M`bPkeVQn?j`wK;3GZGUEuJkve zaF`;TE&{XtNZ0ow?0g7w6|K3wqJ8LP^8x@53{v>C!6@#vZ(j9#Xkti`G9D`#6zUB&3Xpc86pRZdiFohYlF&kGI$F!NeYW$I0toZ4@v&qR-(+9J_(p)qTa6FilRK zl0Y(`Q(XO$_Mk{0e>VLB7E4<|4fp2C4r0_0#exX~Vd@HU14JYeCx-~u3T)H>BP0f* zsulVa7YmvzQ|JVAwp3hAt=BH_X@7xKQ{vw zu;2_Cs!a(~m?3Dj*AwC;5x$1FGFvahWrEL3bXZW41g@gRsH&t3UJ&{df4 zfu-uuCSnn!ft?8WTyI!87-ZlBN5ckV|j=P3ttXA zgu1SW!#8@I*!)Re{|?ntdBXm6Oas>lD)FBtIQ~EW7I7wHj-xg>PIf@nP_0}=B53zY z+Oe8rf<5*UHKkk#TjNi_B;>617mFbmxY06ZN;#((Y zg#4Y!KD*bhMWmnoTriNA#>O84gAPHKbFTC3K$M3mD3modrKO~H05%=qs6l@i3#S~kB@OG3B2kk)c&|Ng&_u2?xpLnALH_9sB`eJ`&bNDAfo?DzLye55H> z@mMQR=Q@)@fMkTAzmOcwmf=5YOlxfA?P}cJU+3-5tatlZKm9S&7vC8{pD&O9}nlA3-c!^TaO>OvFr7-lmoBluTcJ= zJZQ9wzL7>cn&V)KvKIBer2Q8gUF72e5W|7qCg3v53>`e|oXuU-ceiaW+2q_DqmWP- z8smO^WLeqW=&@g0S|}hos<4uixfIgGC_Qc%`f}|w*-z_pVCo;PoxY-^bP-Dka|7#% zJmtM_#()0x`pSN+m7ALoByS$8&M%weQfPbx~!_2K*lB2Zkj2@Xx2ofg5IPVmb$~&6gApc9<+WoHaC$CH+Qx635EOigh%tuOj@`Qp zOq}6?mX((yniUH9;8{e3yo-w<=-&6STeB|0SSCgmmdC(|;gGPq$Hv_Ijvu+Wo$kIQ z4ze7CbYGaC7GN7iW6exu;9_XozH+X#z~4sOHOzj(KBF)LLc*Q9r?tM*S`}(Qxxr-M~H7&Hz(;x9dP z^Kt%9p2Kwa<2hg*w-&jd2Y=BAO4LYEgA<5$PmRn9W@RvdUE}bNv$yMwGHq~AjjM!RUGQILL&7Acb?Zac_3g-QVARGCC~hYmW7^glY!45Q&!xlqPvR^@R=TEauEQ z(orejcrtnDJEx6oMOsu+*;jbR|F#R9;22M9|C$2;>lpMSi z#qzAVq?zI6L6hs2cYdh4uGdjqY}W*aK^}`YNj>e9urPI@t^&a1U{ewWvNUw;>{Sg1 z(yr?2(f|P)K>k{V-;QKgU6lAPoSLZ$ckH~%G!cN3Qq+?YP^`Ur_{jXysD8Ew|2F$= zmm6ZPUpY2u>-_Cg<6SZ3lPR((m)|n%H}eZR*nP^5H7smeP&|(>{7v2)sygbQjvW(6 zRkmhvrmS>DUlQLYsAgYqt!NKcRQ^_eIINzxwtdooAHI>1v>2jMAVXQuZPk2zauoSO zeStlE0T(l2+V9@F8631f!gc&LerVkJG&;ogU&oZ>8_({2aPMB;iAu63|HuN|8=da> z%GsqeI@94Lex2?Ir5uS{N+c%jfp*Th1YxO{oW5P|9yg0EMI{UU|ET-aewUM6XV73! z?7aSYd--s$hMjc7_cAihs3mKB#Ob_`P=*IP^7-Lr#lT!P=Al$5%^yluVlhNT{uZuoGn$RZ^vdIEkIR*Z4SnT+`8eA3qeY z&v7A<`a~(g0FfOZO5P`vlUfVBCz+VMAZ|`$2yY9i{0f2KD9(Sonkb{*E;jCFVH%D< zsJbI`m4E8q#=hub<@D=s3e$xPD_!ioe(k-%ksjg4oTzvw?{@fdxcOXH#oo2O?9#hZ zd{c(BIz!fD)*fd_I15JYk2;d6nc@b~1==bZ-Uc(f0;=%T+}xafmgeVcrzasbr(6HJ zh^~f{^EcsIW`DVS5Z4gJ_tZ9OuT3|DYGj4uov!qco|~CpdB5IO-dD~L!g%P$zA1Y# z#m($Hce%PRWHec^9sh&x$}7%SK?&saPb96?%XJpdxeHM%Zdde1R_3<4o_i2R4g#{( zh;jEdKB#17W7EeWBuJM&K$g0mYYxC+JwqC}4Pu2%ePYy41LUh z6YeTKe*Lc@vKRM$9vM(Mwy&eFxx zqnA7;H|`kA@buL9>R&G3&%-G&p{gAoIV+le!hTb}yJJI1_S-d+n%Q518FedKJW5MCu<7AN!JWA9yQtY`VYIfyvm6aW*8%_tHhe9ES_USy5 zl07-~Ou#tN`{T81xKq)wvD4oqX}_iFod35sA;+6M4t#aEpD%3Jhu(tnb{#kVorh`U z8Hc^~+&vAA7G*QCt_st;Cy2RXf=Yy+idE~_EuYfW|`2hnToYa@hk?-Ls#w3+RxZC*z786>Ydh}wKFh1>pW7WHOn&6gKK(p{91O~ zF@8&`t@rK#Y9EA{sUVrSfN=`O@u{UH1d-1-YDvP>Qbcc=vCIm4%KfHg=)}gXeRU`z|oatqX=82(Ekt5c>&87leD-nEog@IpmaK^=aql`~9>PQ6XX$8K zfe?61)MIszm2!v>9+AKtB#l^p{}3}O?+b?luKLXbbib*JU!tY!JyT5wq0r&EC)aVL zipRL_HdMU&6s^7?MyNVMQ(q3jvzZ2IJYJ|f(U}I~x;kd%`38833hcXNy5>3eJt$m_ zkRa~_j*yyVEkS#7wWlWDXEDd{v`nTLGFb+0tvC$IsKL9^6{{3aWA?%VJ3 zc%=2xCoE^HM9eeBk1|eKKNU}!AD}7!X`B1~of!pMS099%T_EvQq5S?BPiJ7FXZ@)7 zWB_RvlK2#EiYm*gb#u%K9`o;YYcpxZ>lHX4FAMz-BWJSUAkjk`DLei>{T}qD21w(GE)K5Gj@&3cU)T)Dh5E9N!w6ss z(6fPoL3>~u1yO5F&uqIXM6NEYn)fP^CP`~sps5x56UJ6ZGH6T}X|Se)=k4~+sR zZn)<_2DcIDNQan$6Y<1&+$-5f+ldp&f9w;QB*V=hUbiYj`1tJ>T~}X>xFffbba?@t zoKUKu(urNaP6uh)05E#cUJxpqoVmGqP_j%2XAFEU)l{wgTO3f=S&8yOyf+GuRH=H& z>kZ*cch(7+DfI#bU8gI=&#J1ZkiAl2B?<|5>l$*c7|+}|+=RzNd;nm3+cws+qTqH( zP}TlqCspn6hb9_7}Z=QYT$=A8vEV3i{tKg@ZVbI!#krb{)O;5t1$pV!w`tbTA4P1wdzNnZQjTHadL z>Fg7A2Q+$0BLwVD(Yj4;0?HbIRMU+EWa4oRI`(bYDpY&7;pnE5vyQX;Zh$mA9O!G^ z>q|qx{#YIO!%v~|69JwKq)3l&=>odwlO4rvMN0%dZ-5<8QCW#R&X*hM)(HK8=zKGM zhf@5N@!24R{pu%d(A|ICxN#DylM!^D-A7N~Mir^F@|`&Gs=`4nDG2)_6#D92ICHO? zOmV1!0eR==vausYks%ecUCv~X-@Zgk1rYoM=J7Rzme3;isk;?e1*ekhv`)}_YVT4E z{eC#;kR+wbR;9a2M~|Nm_NZRrW^+5@mF0y~AP06n=#Hgo8?vv*<>GM2qsNb5qe4_A zTneX3j^hBwGh{=54GfgR)H*6?Z~XVtyVO9hqemFf5M2@d5$UfluzIWo!&f0n<&BPf zeODCR&K#p8g+^CiS($HAiq)t466Cyoe@QUE#kXwNMV?F6@?R7b@zy=$X)(3m7RbLS(+RO z90{Rm6F?q~2TI>yCC&NMZ!-p$Caws2pWNK_h5hQd8L;_CtibR(2w@+(pP3D^^JZTgZ z6yoCJ2k*LX@}m{?_}b!a=FX9;h!hw9Sk?go>SzNKrrz;y@N$zYlJx69vW3RE_W;fU zl|lesTpmfoJl~6aAQMi9oZ#fFDYP&!U?2ln&e2X~^I&!KNO8$*)$Ut{=2}o#$imD_ z#wx0lN5XG-4MciI;4t4?WaNm~LoaeJcTZb5^cmXwZ8J@aUek$zzB2;h^4K*c_&f-u zZU<8{gf$>0F0M186Mt)K%$=?CTMM2oJdAs@-A1OorFUjQ!m~Khf|qj--vi&0=92rV zJ7T&scM8@fuHX3Mgs5mF(VLK{!#jFurWiq=BpY8M=j6}r`@JAFLL2Qs%0{0)aq151 zjn8#!>d9EDI6LREGqPeeoSp|BYB_lol1vAjvb#hj&70zKC!vGdnv2Bjba( z2$v^ybypBAya0HD2R*Q$yY&HduVT4Sz*8C7&K^ot-f~-G)sjrwbEZ@W@B|lX@OdLd zFxjPsfM(#bXN#U$>l(~LI8J7u=!pgA*V@{Bw_ywFOfPNNrcvUm!D`c+uG#Fod$Psuna0o4Kk83F7~K?9pE^$p&csqwVRZcEo-UxjTg zmeh2{sOl(^u;K{(yOPppB2#m(LooE6|8V;axlPLgVeVO?ZgPF-Td-s$a!YPlf0deA}Zp4sc*jGeVEktved(>KPwv(CQXj7s-IUOx0p%STv>W!S8UDp62d;M~XY zKxszefaG+ThpwXHH&qL4vlne| zvaoAOnYHG;=gTh$Epfk)P1eq>U-KGrU+5@BT+9i#-X>Su4Sz8(F*U&2&~^H?1;?8_ zzxCj>ulAJ3OW9a&BY3Hl!2VRtM-D7910<

Q(0$@jw~B#pUJBz{mENE?wG0%pH^vp*{irgI~dKI{^?0 z)NPAMqpr&kBa&*B=vnP??yGZYo_$C_6nFFC=^w>|cG!-6kC`Gqn7%uM2mK+3CIi>U z86CbOO*zE1FsFSYu?^v}9ox6Rf_cr~H&5Is8wVATiAaya^C7{(d7{-tstlBeme^;{ zN;k#W^J{ku{TWe@g2bm*xNu<;Y)dyJGzc{Vq#0P?1>$M0-!1s8wTESiPC}ihHf4tju8+eca4;Tg=^t|Ya5236P;4(V;J_y4z66w?1>z^SGvG06v z7eXwM_~$3&Y=pjr(|er7>&t9p7zI(-4{&Wa1w}F&85q2ETe3xH`wN&H`0n#+BY>M) zh=fuwhbI>n29V4@7cP&m>IcNG@wBUBAGVYNhv#*#xh?+k;(C$rUX%81#6C)o4_d8O z2Oz3W?*&^|jpDp6evjOq-~J|ucW0T$y$dn-9;KJODyX_i#v1elH!U3{hRj5Ssm?ok z28KT&!z^@pPUzY&b8|mKo(Aadn26gsJj3b3FKYtELK%|=d_xo~h@hxp9j$v3>G01nZJAiaD0_=}B?V4<;2^1%;bX@dzA_19YAD?YnKk zJU;`HXcV~#;Ng)A_zj3>^4>l^49v`fE{=LkaSqyE=Vc}grd!Lac1C^`y|nSzdt}`4 zevYNq>ibEtszbBv6Xi2EBZ*(NyxJ*0F~WL^432mSkkE5}t42ezr2s=oM%JglH7ucap%ZXijtsPjsL*w6pdr{x%px3do;os5(4ii2*Yv)q#%bi+A#$O6Q)3%FZdZZp1dJ?#gHvqQwH zK-=Mvc|-{4@9qa4;%6>@{$=`5<>U8aXH$pH=^C&8u{7)kJ!{VAs!9La+l6DK)AzO0 zw1W-UuWL-5CWJ1P00cpa$7Xgq_3S}rc5LX}+z*1oVf_heL-aWzYDr=}HYC+X;OatWX8*p&y3d*f zO^Si z7SFp`dmcsaK2zJPlm&qke-wX?=CM(j5*E^4Jbj?~k+-F%nq4q1XLWSp?D@I#30KtW z>KjQOhHHab%^?~x=6kv}(}_6{Eg0cnO4uh=VFJqi;yIg;MKA=bq!My^iUkUQL+m0* zB4!*Hq|{1S5a0nCcuQG}Q`ObgOEOb>i;<)ax-y%N4eKbHbY}L|pBtMY;jTt)Z1GjT zCvJkJlQmN|(|n=%t)lTsb8Ped@0U28&&xjuaL;o$-~Ro>!`pif$L}&Ww~$Sn)&&^) zFjms0_$vOR>#6%W2XHdh2+Q z_Uyzq?Ic0tZXL+<-}rOQYi`p>I%_gEqf~K*2mX3o{JE=PN^DOCYw~FQMEAB0+V4M$ z@9Z?(BD`DpsG5)u;Ff`tq4j0QGWYU2qo1F|$pFLMkOx@l#O64U}FISMlzSnbe%z*9YE2@hCTYiS)S}8=DOdJ?cJnnt9La z-~xT<8h_?zt8EZ^+GMl2F=g+`Y}LD^a{q$i37t@njt5__cU(sUH2fc2y>~p<`x`#q zR%A4&L{gcdLb7F*k(phYA(AaKTK3A`gk+?Ql9|dLA)7L?Lv|Tae%Gt>`To`)rykBZ zZ|~Q5KJVv!U-xxgx55Y7MP6qtlAG3(`nw|bp7_L7S?!rI181Q+Y%^JTc_T2Ug@1Ki zt*WYW64ZEU2kJY)eZcV|rK-9gJeO2sc{U>=+QuHo6`E?qu2rub;@e@N?>|VhmwkKS z%fLy)?-Jd9uk88knLq6NhVF?Vvg{xihOt9`&iweb?*p*;Qumd;5Y zo-mZ);F}-*ua|JF5VR`M%?&%^S}iO%T7;MO!kr{BhzYT-PxV=R#^r3%)0yR&Sb?5u z_t?=~dPt@dbFH=hv+pUR{l3qu8r>_nglnJjS}Qv~0T|aiQyTY9)D>Hs_buOZJ84tF zBZ4;GLqTymd^h9`T0iv#3_84jEogmS?Drp^u1sTq`Il&dPYuv{v~!a(S_ZUF-h`zq zAj`+7=0n57t1?tlzqhAT4h#$=25Ay~Se)d;KtfM(at4A>4|9o1;8iN@5kyx5*KCVx zu9Z_`LI6|95{*zBR6`{&Tp?Y=882mY;?9*jGQ3P}6&>3c(u31S22_8L3e~=;m06NK zsSs5&C;C1tx#@#%nAck``;6}wiFVej?{4)GeC7?E2Wb`}odV=ezW?qq^sHMJTku?p z(+;eek(n}wfgih7y0LM*LA#3wh>+50KG~haAnCO?8AtxFMe>sN$x$S%L&&;JQF_7n z5#@R%vUC~fOVUU7V6PSOB0w>swMM-5C$m(p_m&sq=By58k!{^w;Jx28=w$Q$3HR4W zOnoJ%!}ol!R4W%u+B>&5=ytH3jHAziF9+i1MIT5Te5{?6w6n8Yx%GItX89k)zvjoK zskMWD8~U3n8SGVG$C_?1IWEi>d;8`!tj$PtXn>1i(UbcC$x;x~yM5^ABL^TFt5^!F z>)rjbQP_38fIo@WDr6q8`3pidatgCjR32 z!h<76;khO=bK0Jrq!llR{_9=0e`*Ub88@lYF;pp;s!b2uyrG4D>~Z?a- z6CrjwcJn@S%3u0Aj8$njjg1$8?aYq^i{-m5|zcc1!j?Au?_&--NV zpWI@VegU4c8wr1JCFs22;`~eDQFdK-wwM(6m;u+ik1y=mT7_Qx4KdS*?ce#)**L3V z^Q{HxQRXyX93M2EJ5w8cRLrVUa+Q;_o=Rwo`(`~`OyNpaQ?oHt&@GJ^0Yn1PV-}QZS;mJbtk&kb7}`Cm7bm7pV}r9$w$W0Ovcp)Pj=BERc0Dk)qE_px{uzU;UCbmX~ z5guV@gQvfQ10yIuq~|@=2fjyT$g!$N>CTT2;!a<&=d{KnKf~iHr_jTL@(5>M7V`^R zGM0OC(&v?p#e@ID1-SI~(vGi>R7yL}$G$EX%oM2~eo~!HdfoGVWfBP)!?WT{=ZJO* zMc0H~$3M~3+!+_Uq=nOqcmO(8H@R(44no&C!$pC%ALygd9Y1bsQ;k=ga2PCC%{+Dd z_ZO5-&e_wS2yW5oPg)TXCmF#4 zC|Vs6d%bj$@$Z$9j;D5$S=T3~=cPSKBji1E+6(iaSeskbx1TnDfhRyFL6ckd(e@#o zxUk0jVGp*yWad%7Q^G70I}+`Kb61ykyqaLKp_b@SL}((oS76)pTGW^ql;n^ovmRVs zTiXg*h-YEp1z-ywK70@qIYN+y7+)oZACTA`d|LHT9>3GMMv2IM7L+CuA9r|BQ@*ej zq`dAH@np%RoUsgE)ji+Peiq`ccme& z>VE##sQ!r-`Oy+GvYaq}YQ>)vB6}itun~+d;?<7f)k<^-B9Rf@yZ*#s>+~(n)*!Ru z_FE>FcRSaQMqCPVVqmNkdNlH71WR-n{E?o6d_NUM_KatFN@p|CF#R%;)%@=orcSWo z7%S*v>}MRH@QB+kNl^Zr0p>6!QTD)HzM{~KNUTjR9tigYu ze(a41^*E#RdXW}&k)J@|ID%Dlv{*>1bgD{;Wf^~wmVu3o<8J?ohrl(*$H&#l$ZlH{ z&}kC&0kYBv;{F9cKINV_`0nj?WP=GesoUU;lU}uep*Gptx^4PqXGO(26;0aCKzYeG z=S$=aTEBGAo{7>dnE3Z7sD?F8j1kox^1b=Ht6xFOaGvjbB%H9xtCPO`{MeEq(yF=2 z=m?VzuJh-*qS-yG5y(E5z{Ls{)cGBzgoKZ{wvEErEV}0U);>Dd}#jC=uwN6ugw`1D#DJ-0>O!fLu&l> zt)oSny9J&JRf#6u0~t=GSdBN2FH=?P4R>M1*U6c@GU8R@HPWamVvU!f3NSLMx`NE~ z+{mZBI&nInDp0RYZLpu@pjf4Qs-^DMT9J}g%p{oJ+#W)}A$aa8|32zTmqVu;=oX=O z&wXriBmIcxS&E&cO0^}dhqx4-6NYnVb6qQ%a6*C}qg)bRrDJ4fW-d#1^f7jB-`|lT$AL0_FT#GLzpJ;3P92jG2vj5xedHd`acWb5tN;CLVgen;4BoAi# zD?dHnK>O>m_JW7rqaWRWMmhg2PpOyx-pD}j-0P%Fm8&W#qkpnj%uFoDCuO=h^%9i4 z94|#Ub8x>oQe=%|GY#|e{X9r9NwE;z zzWJ1(TU^fK+ZFM^{*DWN3z2G7`TlQzIdg!7!>ZyU`1Zf05p{}qK?C`07G=j41Yc~^ zT(F&O3}B@lnv}Vq;FK4bdiKohQFKcB_H_2Rh9|XN`kAIrO}C8ArOTKSwd^=v zD!dda$@l&57kb=H9eZ3NSc%Nvp{xZNnB``E63(JY;+IG zO(B!C7~i!6BJ`P;GH#d;b1qa}8$P1LeQ=9+#Yfqku-W|K$ICkyyzgDf%=-B-O<{y{ z0r58bYM399H2`7>`?s%#9#oKg7I3%{93Rw`ITm6U>?9a^Ipca387Wytywg(4NeSPag%%!Gs(x~&iq>hmJa_`Qh2LpDw}2m&q=pkffj*No$SXt zXAC{1W3sXv@}GmBG;h;W3v1=MdB-zmE`LwR$NhH$vBzIHU{5^jKB;GV=Yx)D%WC`% zyNBr`9cEG2zNpQ84%3%?lBV!)6I2=3tES+VLlq2++`<+P-Xhg;jk`o=tc9Zxa#c^ukt_!}Jv2LH#Q<2Yq&iCxQ@>GP1Azi6S_-sWgz9+zk zK|J)FZ*RJZtDxdLQxz@#@p}O$OHR@)idOCG-$zgzSvhW9ryXuun9V2R0P6Qv0f)pO zPA4JtT>zOmP`e*ge9zIJOIGe*4M;=x`T2Ag?{jl25W;46f zyw!YDt3e~ZJ-PK3xV0~BTc>}muCI^G%_+&q>_$uR_^DHmzkSn(7@WxBerpjAD8&Ww z-ZEOoBelU`6p?y|XRhnvY>nU)BWSaSQ>$gL zpJlspSpAZpdUbe)d@d`8x~1N9+kOkO=b#erd%GJ{;^iEgUcU=mN@eQT81Ps zKmF8JpMFcn&Fi|lL5*Wss`j9Q;@RURq$A%!pY4;0T?7Z(eNs|VpNnip$ha_M<-+7X zqz^y=Lv1Cl7a)vtzvHf7Y#2u85oddTS$^HGhH6>kb5HH2d~&5f*LBtX@hT5_8RBX$ zfeh`qD;16_%E7oSJhbX5LsE6mSrz)7u@Bje<&o#_SNC;I(swx3pyfOcC{Q3AB-oj@w#W8!8a;m||tQ_`kZ9~6qE~EOdYuc03ie+Yj(vzXS z7dAhc7hvMUK1genbaZIJzh+@!At91eK);tzseo}`Rb9Q0o?gM)x}PD_7LpEvC4TAB zF3`PZbKrn_(jJL8F&d^X3RI;OjYc|xtR$s?_lrWxFsVg!dG&`uPK$2P_aJ?BtrunW zmImhHsGehWV!PJ24ZFldm|zXat|(3buew}z`V0W%a8@&OvxXV!Lc7wu#r?*Ci}p7c zSOAd(SP3SAO4eGMU6?-N^TOVJxJfLD##Y&p3fOo}8YsIr(*AeWEH_-Zu7vi2JDT87 zqSS+k0vM^%ya1KQclVQt(a|!PreRgzx*cx}gs7ey9naFzRCILwz+pj8)qb+g6J2Oq z=-rdm#W|$-i*<6^Dw~8aTmm}ssK13c(Hc^?%Xo46whYTP*lp; zg(YQOwZ0o9?d9LI4)1M93%qSh7w!rk7W~}*`9_1Ej=s)_8IrfP<(|TPm&^1fnmzSD z8?w18hJ9A{VR^Gh`7w7fzu_)8F|c1LTOblx(1V~4agnCp?j-%>{Z?OQHVfgQ3YYOu zXzj}=C=itwI^k&U$N^h`4FxBp^XDVexzHv|YLl{rs`#0j@%}=9Dv!h9oj$ z5(kpr>Cz~GQEQzmnkb?{=r4(KOm}s56r>dzP)(ZcqQNfpI<(JEtOj{ipFse^D?6V%KibK+|C(pZY-lkE_*xsr);cH;wl4`R&aP#*=U(2G2mLs&a zL^BEetJYh*_A_51@OB_AJ_IF$RfCPyP8fGN1`Eq7C{$Y(w&xjLhIIELZf&aC>1w6rcQkE5({>H1$O8Z8O7Ep`x9liF}!T?l|AAQ9KUTVlg zRXd4>DcWvQ>0!>6S+JemiPpO**fJZiv4MI3ea{+TjG&|Ca~Khvfxa!^Xvgs0puN4V zJC{*^JDM4gA@pgxuz;j}z{+>UUf6~jBn~21FS+mVW4Sl5Iqf9K;phcD^G^1@GVd4` z6VANr{L!l1VFTf~m2AXBN>sKAR{AkCc z<^DHRilv^{q=}ET&1-!$3J|z39=d*`88%Z|d4@Z|z~_Xf2M9+X>_=OnWq6~*#(mol z9SzXH(E8mpz{$OXgoJSP}=KI>zd9|7;wGv-9hX)1!KB2bcFv zw{^FE82Hd`BgLt&MqA%uqMNJZBW)l)qk6z7kXV4H?M~x_F6&v9?%J7MnQX0AEW#v* z1GOxCeZNS9>veT6Fr{9jtBy{Rq_y=~0H!9Rbs^};Xl&Bcbe{rMf?$P$SLTPdd0UR2 z7^upp(%Xrh)D5IgkV7x5W#VJfQqt2+T*0K?mn0SGR2gn&XTjK_J%A6!U+mHg*&qH; z)-1)t%%g@TC}P_b^V!o)d(HWLl9?o(XMflw2Awo#i>s)ynXy3#RpTno7OGI0OGSbP zDNbIbmRtuD+7VnO@7vOTcfj35<&b$1q~KH4HF zG`9S7=(&1fo=gC!U$APa1)}8}92MeE9L^Ck0xBfbS!CK&CHKe3BM;o71PM^a>kuVN z4_BCNKPirs+uOS4jpw827DOeI@tc?^3a_-fxOHWQ(tZZfYEHKu0&&DC1 zUm#6{s7UF43e@3Qj8r-I-a~21b-ho|UQB6R#CJejK)^;@`v`iA1S<`cSQX1$kiB4UnKMt4+u|M^%0@rGw-WT$@4vVg-XMFoVS`|TKy``a{>-EIQgaYjE-qlOy#omQi6v%IX^UXDqnd*HTNO2XR zzuX~?czvlhuzYGpor~ICQS5nQg5QW9#X!Om7a&Vmf%1Ti%EmNxJiWahv;=2xp_SPq zuEcPAVlJQ4eZlO`kq(LZB=EO0}}};(DM&8!s0IqYv|h5Mi`GZ_ep^pCmfA z7j}}OWAQC+WN;qHbWHm#3M0c>YgN~__SUH~62D`|`-Kt^%SDLq_kKsIm-{1Q+fMVS zl7?T9$yxIBU&{HUyQ7T${wBW%(RM#lX)1G0MF}U#5=5*EAmZmic0Buj(;c`c!FtDL z*1S1drC{GMeK5?B*+A`xGt)YCXk!?-=p9>Y-vuwpjh+UXeXTar17~7C>X$bvHmN>& zRcEI^aVTBh=kV!|&59jP_bmv)M%Ro`2>?(?&P-!#jl31B6^DYu2tkur3$WFQh2nQ^ z2T3EH#4mNQAn5|buM!jAilRqiAhF_1gvQw}(e);6JlemEEcP9szs--|$ zi3U?mSC>0j`JU;Nv^?*^m&uCM5UA8VLZW;}ExPN^HWSVD-I) zmDYDNNhyHK2{n7|X6T|=zb@v=)*7?Q76KBspK}7oulLg|vf@CKW-t+qJ zhEr#_lBC#JWiI6!|B{iQrv0nDkuDacTH!#{Nh6 z)JD@q0evJuzHn5jfCi`-+{T>AW%wCXqR+?1$vKwe@#i$g`l&h8yf*=Lh~${lmI9q7 zVDCXXFPCo}!$PkJyE!VwIb2mwrgDbL*mw9Pk5%Vy!-|{SdGHuoX@O6O0d4``0p>Zt zV~9lZA^9d~!q8Yni@5T^Z0Q2Bg%y35&ja4Jg^56Y&7;Ln~|IspD^v@+uM?BWynd#%2W%n$wX zn!|*O=|=!7+^&meQ)N&O>l`Y zyaOHip}t}}M=U`+;?o^%FR~8-zHt)t%V6nxb;G}LY#5%IEB4{+LDx{&oI7kFV1%!J0rU$M00!uXXGNB~h^gv7-)!sY*a?`sy^+f9(DQFE60c)AiNYrv%EGh37wPN8YzGf^{$9a~qPh^8#o_0Kg;g_STVd%)=%A zAA;P4J5Y1Jnh2^Ep+3YrA&yMQy{;q(Mu7Rh^yPNW0;K)h=Amf{i;VONZs~k^)A}#n z5XhHXa}l}|-4=1P(%y?3Pz@h*<23GRkcLgW3*^DceA4fT63LRK1f)kvyf z71ACMS6u_eD6cmYmkpi@R8NLzpb`XX;>ke7L5R=@ufzOXttVg_)LUe85(FBl^mcsB zM^IX%xcE|jNQSBQo4uXvWmK=5%yl(V?*Ipcxx`XxiKLYut$MURD zdA%+9>gpM@FVF6jkfd>jOZ3X$-FMSx?-BKIM%LZCC-|MHrNxE0svO)@Mx71{lX9y+ z-+kiigX47vpD1YWEQt#Wc;4i+`91Dp+C`l&&pEqS;Oki*W_rCitWPO%x;SYaqsDx| z-7|RIy9(DN%iE7GdhZ~$fCQn@dFbn?OPe2JyE4KK?0%k?hb3n~!`H_w#U zJ>+H~YC-lyMW|{<$YJUrC~c08hpV%1ltu!cgVZdKc(qZ)A5qKD{0W+e^JdOU+wQ!N)7Wqc)(xqxw`7!*923>;s?;>PnUBj=M&z?jmz<7kt@P>2 za==|`B=w1wDX;8oQ=fdST|wMsO^5j}hhK@Mfed1a#36yXmW1IJ!M_Fh2BMWHyJMab zD#Q7(&n<(r<5_w-4-P8`jK99UamUym_X@~Y?9a~wVt-dpf<9yf(M@xtJH@LXN^+_p z>!odHW3Iqmy7bOE0UkJLiUieVJ@`HTd(?T&QMTUuM7FEXsSY2mBSJ3b zoXjsRZGW{)rjm$)aXS&sGSpwtYyn5@ouz1moV2|?FElM%FaYpXpOKBUbfmPg&?^Dm zO!`+{yf>CghK-gCUTkVOdY{NDMyVePo!RVJKi|MWVt-;u`XR&&(YXJH8e}LmW7#F6+>7H zYmQ@itC@zzv$CK!use5G?zyLnf9YbHObE78hxvITl7cCOc@jz3VFc!hfQCBP1bHpC zPjdHk?|Tn1NC0dLnv+$2)lIs=nRX)?Ex+yZ&h65lW`kE-2xhq8DUoG&(+ek-t zOg-lBp}%4}N3ONDJq4LUxm#E~zrZ+QCyK(k;9$2m)}l=i?CK?hZlY0?P%<3u80xX!FBl z4BWd))aOUU+=Y>ORht(K*m?~296QLQeuX(qe!bKW*#aMoxr;XcuAeq`{Rde;%N?vS zOTsW{-3y{?2^?Qf#ytq!)pjPGAxF2^ndi%*K3|+R2&fQ(5>YcljU6=c8q6x7oH&w= zlT)ze)Om}ZCm0Xd3S@N)Qrl>Ln-hn)u7AVi0~4eqOhIHhI6Fv5QxNwF%h#WCgAA0x z!}K1Oz4@L5tqpgxZ~y*bixSs6?cYGm%mws>cy88Ta-Jw>QN}uyM-ie7;A??k2+som zAfCJXpI@5r3#&lT@FOSx-EmdPW?@(wGuY?i@rM{7Qe?Y9cA#eUuVU?4>Chm5F@{{U z*_c>N0Pgu=Uv2jVq5ui7KK&y!@u&P|azyLz55KPXC)Q|Al&2&l-$=Vt$jp7Z$^Ld* zpe$S1;SeTzBforCQ0>vt+=lli`BI})h>n6m^wZ-#2mA^It#^}=r9#iw?81p;1Sy9u z#yV{waz{w6Kz<=oBP=_@#TT|?4SF7bszKn5pSCRs;xJ1h&|Oo1XtZh}_nD%jeg<1g zgwEOL>tTrb2d7`05Ekz1y-?G%>_}aIllvTy8I+Z(W}N4VWI2nKo!t-Kt@&K#Dxn9) zEM9M#=w`23d2rP};^blE`S_l|)8w=jnXvy)p?JLq6NvjDkhpK+Q0#MMMc zNB;9QPk)HhEem`S_$5G`1bHwKTtIb1jFLs+G2NxroFvu)#qVKZC+oaP+CVR;a6UuN zD0@ZiKHSXm;O>aI&0M|&C^XFHT!#4;R*?=vs2fdLw1=~Tyn;qgFRkS+dxPyd@cGzI zCdxtPs=$rW%>xIFnA^T|#0GP@KdyOI{7a&ONoA)>_8y%_`HCRu7evI109P39 z{oIFE7$STXr_BajT-|`TZ-Qp2y%3q$NSg6i0dU(N-&KjeC(! z_k>CAQG`y1$Y(HxEB;ojIy&szU3);~@X)OCi+4OzJbNwqqgz@$n)`p363=xjLAHly zDFbm4&_DTVyl}v;jEAqJ;CeJMPUkR2@0CgtP8wNzC#hc3)-BZ$3kyX!CSlZmbV(KEEM!M1ZiDD2hDAk`*HM#$*2OOrh+e-0*`RB&()h#C$md>>af# z0?{Ipmx#(edz0~))4Yf3CZ*(SuYug){jV!NC6#;Re|5a@{;`usk1`@C(8`A%=nMdH;>HKT@y9KUO+0}gOFjxmt{u)B*K+h`W)D$J zGlI7YjW^fxClbe6+~RAwk4fG>eS>G7SDp6jpTZ^o!7~K!IXVA{$#avRb<^T2eY3pv zj!F}5ou1@Jx6_h8y{3ENIg^r|BY%q58tPM6J5UlLJ}9kFFGvWcWnaQUh560~NTV?Q z@f_S>5x2)@bBPONCoV8t+F+>k>0C5}U{a%Q)MUn7-AV1>&pbIS$L>-(db6DoXXbQjdf%}Yr?TsU^5;)C5h(i^_xHJ-9;Y)CGo@%Qweb|P+_U}txr?=_ zQJ#>a*i}sA{u5 zai^vim$w{Q+aArpmsD(Ex3D2QE9(Ru=yx` zQDU2crMx?t?NXuhfCgm90n@z z&nJMo$+(nls0?tHS0hvr>l*p!GvMocF$)xwfMn(HK1Slg87&n<51fz37DLb9eQ7-q z>p~z^^ig3`98R9Tm+#~Fy!d$eE?hFxED`Du!l-tCV#!+GX{|e=oyAeXIb>fyEzw=} zsxS3DiPg1Ekpt#edwNDny{W}d_l&Zb`~tl}F_}OHptM zET#C*@!y`dh*SJNHj2Nv)sAG$m?QQcVmkHnhFSPvU4L?N z!1J?Z(xcWb3f)f18WEX$cK1y5;%8>9tLTXvL4s7^)u(^Ha+WSS$J9aC+~$Gh&}Mk>G>8a^F3+h#iHlN4;zvi`kb%b zZxqB>a`tx+T{&+xk0;p_S^SD%i}SC|_s$6u`Q64pdiLb*`droVn0ZaQ5COa@)=0(L z^m=gi%IDY2*5qW8heF3=YDAn@=B0igcysQpjpHrqsf0Sm9>jz>MGuw)BR_T~D14yl zw#Mv1jAgHY|H8SrrdtI%u&0o^ew_~bVEciMKUN+-!&wAp9Rglr>JIiuHIi_CK-DmA zgh>;@u)!vSBM3F1gm(#IGXLHjldPi8wT9)o@n=JxiW_h$4KCZ z`o#L{%OwG_X4^gtPAR>6=+eP+sDF5I5ADP=alN7TA&5fOvcac-NDQ zljwYo;=fdf3{QjG?s~cPz_hUJ)G1Zrt1>ESPlh8Ta9Fsc4G82kygE@wEA^D=$?@A% zjFD6mMhtP3<$QW>FUxOP(3Z^%tX})m+QaMA`%d{w#H~BkwZ5CK`IS$3!9+@ES&CRY z$V!@PSpCi5!&BZTIiG5u@rJzk#H1GkM&GGx5rNH;m*29-T?xKo&rUB$NpF(YTg+8( zLqWAA#-_dWuEMU_Uh2H=JQBYvA>eJo5&A@~`I^%iuQ2@2xFhr1~$S-?kx zuRcckEUc_x#0CkRKPQ^tm#{@lOl;y74aR3wdx@Q=4Fp24<;UGp9$6p&-XHZ@@4_}+ zGQB+{CPo&J9v}kIR#fUZFb_Qy+2-OGK zNfV-!w={QAb=$tlx1P^;P0lk@e)^$h$~(A;Bv8SwHh@m$25w#p7JO;R@_XLy;1M+9 zt<3kLUgvmm8oT7N%K@2RSH|M1tbmyn37ukOR|oG8R<)3?eANG7@;bR&?~aNoU);I4 z9J93N8mSt3Gsm@Fbf&1W!x0CD0#L5`zvCjZy;$i`uptZO7p@qHf8VCDE9X?z{)&tM zmGdWLL(&?CLv{r#m*(}1qfxS3o?`J}7$-V!}<5IhoO@V!2^2ec2@ zAJrVt|1k!8r`wPABfOY|N&X5unxZ+}eC(OZKVC*N1BlPWR=_81cYed8cm576=zvnck{3JtS)6JxG!0>%8kxoAtEkzfV4< z;tC-?D#4}_`Xu24WxB^V@%~4|?N-w69RXhsUu!0C?+{%(osruQr+hW-Sqh>i zA+mmP;+o}>3(TF*jY*daEzy6B)W6ipP+eOf`uw8Gx7p8f1S#LYg_9Ye&*f>chCNMt zQ0M&nZm9y6dhqhovP@sqZZ>ZQYli8KeSKnegyurP`?5`2N3ArRIYZO0^@yjL@jG>u zah~M=`7HCl&t841FVjV(k)*4jWpFsS(<4B;zyz7}{cKKHZu#Z_*J5 zT92bQK{mwtEjWb_KmLjwUhtX<2bp%we9;h23s3L~+5ii@G_1x0R z|1FO%#FMIc8UK>CIhz^F$>(4;fA8nvqPN?Yj;t?hmEk&Qokw5iMd za3grEYnqGizejdA^z;J=%clzn>mXd1!vZ%}@!Z(nnZCjQz7wk9^OYVwir z@$k}0+n7}9x-adw%}gz7dCs+*J!f(P=N5|n|K1zB0`cD1(~)&Gws5dy9rtVfb$Kan z&}x<&Y%E&CGL$s0^`|yV-8<>u2PM>KWT8*V;!rAOvC?yoOK(py zxiq@Bob*paKVwAfm*?ZX9j4kgCOdWc&RpU1~O&jxuR9ra?v@i+BF$njOb~X!_u_-TC6mmX_xt@=ww}(Ubh| zcbs(kf5A53Q44sTd`!i38zPOD6*`}?MGh1wC?H33^j2d=Rj{{}obl8Cn#FHp9DB4M zN+dsx$vnbde~9eg;x}&?9AvPvn#<>MzeGGv^HekUtJ|`-;K<==gY~sW7Ut z&`Fi{UaH&&)uxBq&%RGCtzd zm9)1&^>8E;k0s`9U9NPS^jAvR>tN7jAEHc!(J?P(+{kX*| ztHs{Y}#^)Ww#S z!=X+?>}G&lqGhYZ({RL%EE#`~a%1=@k{qdwDnB0BB?=Kv8+S-DR-X5(eC=?vT{!TC z_KSvX)vyNKe+WQ+cEEzqh%YXo9qn!a<;||pKz!8S0`nO`hVAfHNA|Xb#(!sO>vuPW zoNlAn)SV7N5Vx2ukEF$P4-Rg=9q+uwi>AWXZ$m>5a^^bc-l0#-uCC&LB0|m+6Udg% zztr$o)qb-H4`1mtRf43Fo<8BQ5_Bh{(;S^67&tYX(FWFxt^Rw&IEEj7xzDOdW2(5AiG?%Z(oP!rR)(gB+jcUCRm2=#TYq;_#gO}kLhj)} zCdZeF7h4cl(o<=7o!jPD?-U4Bi0ji0Q z`qo?%GQ33%HfLvxG}$9=6-d8)bdYo>`s>Zk^dw)pxhAY<#LLptP4gZ(c_SSw9G)t> zAon=DDkOSM=+Vb-s%_OFA8d-2J$#Ks@#B>#a73bpx5fMJ4ZQRS_$!7vBhL^_T3=s> z^(*+&&59p*X#?#%62X_}#_%6#LR=3WYM zpY{=UE-oc3o}QXvRDc(GKeObMU`Y1s?0eBwU*~|59~L@q}h~5!|3l(gjZJhQN;T1r;^^YCoP~})x_CZ z^IFogH0RLByLSvTSdWB9j%#QClGTd#gml~|h^yqrIzMm6aE;?tJE$gh)sjCqb8n}0 zcCH=lJ;?WV?T(RIo$B)XKk#kWP0DX0KEXM0nr519A3s0PmVG_{`gcU`+pLzG+#v}G zfr{xwfsl}J{iDI6|e#+hS^gJs3%Bl(Z#L6a%Dc;j= zv(cCFXnRtmUTXX|h2c2H4mW=-Xy3MN+inVqyP{F@YFJikyJQvBuClQu#*4f_>3e>n z+3fG;yV&^nl-F@`X$d|)FGz}m)-y7;5ews#R49eIui}I_RvX%P3d{ntIoB6}d$2ku zxAyo1avta7OY$Obs;Y_g^>0McDG}Zf+p1c9V|jJf0Er$T6NQ$(#94~;Ho!3Y+Vk9e zyDU%Xg#=66+}n1=SaYAs;g9iTb{VFxG}pu5(yUIL)x z(8$P3ckeDcklcb|9hzsu!f)Tc?Wg=q=Su;V>*+L$Me_4RD$ z&ZW0czACks#zTRs9W%S|D40RGfKMMhCga;NI&=4)Jt2*Xcr@WLK*;%1RqA~Y&;}j* zHU86#+}D@3IZ-{e`;ajWf>c#?Os3x8l|R4N(4P(W@d@C_J{P6lH)+8qqwvd~nWH)B zjGP=3bC#^)a)srYsh>Q2Z@;^K&NUEKh>N{7Ae{8{xS5#>ZF@?3-sMv7IVBIh)JtoifpjR$!4#C-tv3d5t>jBfv@+2{Qs8L!aI>AO36pKY!Z) zbh(!|M%1Ev_AsIrg}MaVxJ+cOzOuV750Xjvgiii)K?C2Pi3gJo{QvBtm;LbL#}C6s z^8y_ZJ}|?W87;b)4?fVjpE&Ulgs}s(v{OIq)o_?wKN*#x4#m8YRU7i!AZNQD(HAZU zyV%XfAL5I9`1JVcz@u>)dV@c%T;jd;L`nbYG`*c~Z0$`|+hq%tloRJvntET;Dkij9 zkIAV_+zkow02q^% zP`|78$d}KbKO45w(F>9?vTLGlCsxzH^40mqbQ(Ci@Di`DtFyiuc$t9kr&%9r&qmE&<)#i9_!iH{#2mH+rU0rHAyJb&^>ldDuz`kZ$!g25*m5^`bo zx`NO9_DNxWmyS}cl+#RU-@kD(KfnL>4*44^tEV@FPO7LZ_Lp=;8R#ua_Z+eiE(kd; zA9K3ae5@d&TZ^G6QNr_fsyM6iymeRQ#_2V^j33qB&ENtM%RfAv@XtZZ+g?;rPL926 znDO}W56;ZQc*JT7W%KS=3b1TAcFa-Za=b)`8jZi_f2;%s12C=PuIT$^v1CZ5 zfNyocm8It%#_iySFDJ7)KRb&Av@_}y3%V-lhAWnO%X4Q+uyj>eVR4wWJVXA*%;=dk z*@4zp+g#QLJ&j1e)XVSImXsKlXO@O?-n+4~DpT!Kktw~q>r3Hv?IJf(ojT>{eRT-seP8?NvBij2o+uN5RLG-`cKIs;Dc4mDWl_kf>Hv#Jp-rGss z>Kd1h3zpoU?O*!ddNTO#%wETvgG@w55kD_Lyd5AWrIne=1Opz)owU-yr%Rs&^x9AV zik>mSfpu0}Av&BHIT}*5xUOA%^Zb+If4Zj*$a4VRKveL?JXKha_yz=oPd9i{jbZF| z6yP)gE0A!nm)QFPjROP!K?=>GASr1ek{E@-0{I6%b>T9!%EKCtfNmr3eyNM{l+&hir|V>nrQ?U#;9RZ;N4~<;B9%>$Wui z#6%Ih;*|km-dLfL(zu*Y>3AcjZnDfvG>{<>s{)TA@J>LZ0-*1K{t0>Mu7p>t+WQb7 z35=AM_bGHi&0>FH0Ad+*eK+I9^mZE@6|nOFnfJ|W3Ix`zeu7PRVBm?%PsjW#VsH%z z7b&WlmU$*vSP{TZ#zs5X$ZvP=$~TZi*kg}9&(r$O^;m!DL~C};o?>2AwF@|hH-5ih z_*!_V;J3<)nV(Y;jZejU?SEwCEJ(ICQzo<=GSHiEuTPqP>x{g^*7$WVL1pmx^DAoU z?r;_GB~oGv4;R-ft!*dp3=z^Dy&?Kfi%d+^76MSlY%=H9(@(F#{SCEV*T&(O9Pq~x>QJL=7_O~9=u zZhl^3*WNmVp4@EOub1UsUFLd6yJBeGpyqXx9Pz6VbiRBzNm0=8e;{}gc(#x)BS$BA zS(pSV4&EI>d<6H(md%SQ<a;HspP=>lOK_ z^Xk$`qQwF9apJE*81&Yd)eUrs7#XP5b})%3m1Lua;UXx5z3*Hv5ba~ABPvW@x5C}m zbU&f(0ck)gmY0lihqPo$qOfyY%m)!P!9b>}tgL(lG;bdRgAyTN1S>J%1TYwCJj*oe zkK;{$-F~UF)&{$_XNEmk7-{zn z?jCd*G9N}a+3Wn_nv~c6w~Q=CX00u!(C;MZW|-0V+GEolriM5G!D|)TR3jnj{?ROg z$03Zt^Ybu1<3I2YK?0c~VUY&H4KaoS%h|>;8X$*}jBUlvOR1p(b`{$Bryjff`b0dE z&A$Q}YK{Y}yClL#gT>wu7bWCqj?HrYxuOCycmC&og9!h9*`8vT)V2Wukj>16z}T?dEk?Ebi3=ox&P~Gs0HeZS&6xaa}mG+tL4nah)hQRVV*Q1DIlhInR%;g~8}s z5M3&eWC$Z-!l)es7@vd$0Cw4Hc|kzMXmjV$K)L5vGAm!V)O;>~_rPIVajC~BCL%VP z=zD@P!;I^H`R0y9Ys>>Qh_HDb(GXyF_?qG}{rTeuKWfuxUbYBne+lOwdAqKcjE>Fb zTFMW`m&4oT?V6R{Z92f`LxjEngX>4&t4&Y~3HmvRQbb!F*X~$*iWxnJDuP=>s@K5e{`vb{zqzt0ueX436jA;M#5Zo& zWed!{kbq4c!HI>hX-4OiTyRdZDB$;oXGXs1Z6`wBQHjz2$h^zCk75i;?e3 z&LGudFe6#8=xqwac=v2Zcm3!Yg@g@9YL&Ot>VMM~ zFtK4CD{R0%5xNjCTt`WJ|KcGWxnLQ;w1ys243xhPOT@TY=sSdxloEww@IMy4A=jK* zi@R42p85!U-j1W&22-&?YaMk}>hf#G$;zy^;C+oSHxi+#Ye1OZVQnCLZ3SD3(9pnk zthDkV@Zp@Agy)ToOZt69T8m2&9M>YhM^^1PXgms%Z;c41M}D2=#*h!4H+5rc6D>(C zFwCp{QU7z10l&MftsCY^KNShe*>Mt2`O6(TvM zqOwl}Q5h!WIMvc14uB`!*87Mni-Vhg51}cEo(l0IdCqN}M}M49h#zyC7sp`Wulozv zR|bn-I4lmTtMx`Yu0=^>76ri~MBt_qu&r|bnrpBfH8CN?0|ipOXITiU9VpZP^A7a` z6Dt-RWxWf7#Iz33=VY+LAVyDb^xcEEc$B(R}qw z8}lVV7QBh6!gvqZcuwCQkVFePT4u&ZMIF?`{6JK&pmHMbyMHNF{6%LQhIkMJMW}R$ znt)(`!Eip!#j5j$wy2UL5WG)`_d>>-6t2V`l+@MzMd$wVvM#0D4^a!!9yPYFjdgYU zm`g$|v?yH{Ut+N#GN>l>=5R%x(QC$9?tNo+H8a+x)OB-I)Xln@X>X%In5 zTDk?hx*Le9t-G{cerB$KHFe-N65ewdR`f zi`q9+^+0k&B=mTl3)Fvt*H0?Mlh!X*<7I(}D+PcGSZ#hkTQVl%4Bj#UsX0+NrmNxY zfxCu{+)U+bvX^Zi;3;APao7i}HnijFs`}1=#={P$uERy&4Sc9E?LR>S32Xz$N=@wn zNMU!fYmC^6KrjYUq}wroD6gp3IM5G$vjM`OA@bu+ipCIm1hO<0p-mM-Sr7sMK-^8? zQMY1FS}!@90ZzHnngs577Csn7`;37Hj3#bFRt6R=3YInVc6C7c!b@ndofkvSM4)vR zPB2a$b8bo{Y2P(o>*Na+U_q220G^Gkf8<<&m(&!p#@AlLnOkdps0qx50%`9?2xNfT z@P;E00F>_y3#VxOwOThj1#apC$OC$k2)G!ci?#YNCZ|>*{aI_jdqY8uh77!G$4zvA zU}^w5YT11nywBnDt}0lb0*T~Zg4f0G@uNbX7dgs^!38W8?zJNerUOvMQ}i@fwlmzB zQ#jr4NOfG(A|qCN9M$n1j>c~INey^E05gC?G7`$vE6H9Lu7I9KFx&KM1(e*}@emq_ z-XebPV`T?`W?zE4g4_<2%~25VHw2svQX!Iv)fi+yNwQHSh>A3#A_@S#Y@J#Tz;d*+ zJnMpW4jmu^c;M$pjh74@yPf5~q7e=&f=z>XJX&Ez1XT-S9=rexpYuZx9+Dg>?30h1?;TRMz6+FYx~8@vnj{Y5#qm& zT*$Ej(U%266TCdt_40&Po6=F8u`DixwFP@9A}Xo{L=Rq%r){aOIT|-EuP&|SYPFUD zWDhJiya7B6Z#xTu#5r6ZV%7<&I7^;Y@CTrF6M`^t!G-8BHl>k%rtwo67)u~x%Fs+Q zofCr;`6M@4oPer#ji1ZmN5IA3JY9~7iHQXG*t^hB0^$bp?mBw@=^b1P;^|oj8VGp0 z06iC&8S+X>{dY#x{$GiBU*?kThr-5y6bATM-$4*0SS}0ftk7v{oE%}by1aR_3mFJp zJ{g>3DqlMQa9|9CBxIxrJ(@VU5lGAYf0@{~>8bwzA_Y$GdYqk{Kw+k5NdyF@)-S(+ zMgs5l3bV_$KH$^+Kqd+(rtMJXfk_2Wf`i1;4M4H^S)YcwS|_p=yVpA5sFQ{>A21Mz zwFEpVXdFsExdr())Uts=LBjc}15jlFJUnNDZX(F$^6%KkUs8u#M~==k#{E=<=mH#M z6*rY9vq=Eni-_sM8U`_KRfCBfDAUonKxq{KzbHXGxG?IV(yhE4)_UCsr#V<%2gCIt zj&%+pf+>~9)t}~E<4SzSiOBD1<204kBrDFvN?J*uh`O$y&9R287Zh~hUP&8ha zYa&WLS0JN8SRzSsYL?1D2#a_0Wf(xLY`gycTXz{yFV}&GIIQ+V|1$?@ix{iDjJyY< zoOP${7g^@o-L`D-P##0q5pM39sHp0baB5#bN+z5&&<6^(OJ$%@ zg`CRB&a<4h;GNz(JVXh)c*=t~iy<;JKH_U5n{OwkzYGAUdj+cQsm!B|l@-@1r@FczEWed1(!+6A64UYm zE0cAXsU3|frTd|ddbP6c?)vLh0T50{ycwS!g zw6r5}wvNAzkJ7ZPIXO<$v>We1>j328Aa%_`n-Eh$*bqxcWS=3|MKV)B9P)^lzD>LS z7kSq}I5X_xH0#e8-7C3ZOkJ?pmfq6>r%-YxE+PKCHO%+0R#}?*x@lLg%Z85uWgcpX*b76!dj*5FRLFt2 zxCP@q&;o?(m%X~H=XpW}?7)W{9K+QiP$JwV z)ou8DhwHQ^Tdgp;ljLH_#x5Hy)8k90nD3luI&X+%3t0qf5Q47d=7J~-f6VS|7w zh5DZZl^ZZJ*DZ9o7|u->sP=G4-9!hhG-_$ISC>BoimNs&{ZxSTQ{G%FqyqsYD5R?l z&0rDM5jVb+Y3QFq*>o26Bg?J1L z84^JZqy~p(B#qOy&hP;bvISMl45cI_euL2(GQ#!w_6$&ENM!<%i|_LMU*iZ8^Xrw2 zZtcby?3veP)VKv|BO3~#>1QoJ#JyWB3zJQXN9be4=?l{Xd7t1dtPbY~_q{TL4>SHm$vhWIinE8{{opj&*G&)T$Jp{8da06vihE5g)Q+eF!J5Z`kE!Wkm}|27r|pH(e>EvOlI*izyyuCay;qdgNsl z@G7*VTEn#(!l2cZCBGLF!q0$%2*giJpsYmFJZLxA1N1nqt2AwFgV|J$YiH*T#iP87 z%om(#6?rSMBl*o@8=y&q@X*!)Ar4$hy^$4NS!gY3fFl*;9<`h0+uJtqO1az)zm^`T zfzShdKN8!i9$ONdns%Mm*7cPXoZl}Tf<_6zIgnN5^vfNX^w2g~ggk8#S{IBJGLjR1 zk!#P@Ru_V3wA8w0cE-!M7FGXy1;B&<1G?}T(2L_cTQ41Hy(xMY2r1x9$utA4liDFI zCtx<4k$Y=w6ok6dI%h*#$C=gGJ~^+0$_fF1$}$lQnG2ZCf81oEfW zlOMsi@qX?4AJvq`yqWw@&4$I>pm9(F?OezX%jt$n6k&W5M5t0KaN&Z|(7>VVN)?oA z*U$Ro$p_%UU4Y|PI8@m1Xb={YD>$4ikCkTaJ(+}u3te?^)IuQahOF@t&|^WE;l)E? zB)o0F*8nCj-^oH05MU8oP>>h_)oxQT`~b141Q3q^Aw{evaP5KyDa=5Y%An8z*b~g! z5PKVhH3~yQZr+tokRO2f1X7iOxD6t;03*awlV6A{CDQjrY!KnMT+@D_2V7ndv4JWa zIzKSq!M%SU1XQ>IH4g_EVo-_nA%WD3G;e^K;{xqQa3Guq#_3}?1|aw#wm2{aTb`=r zgkB=2$ISnQ@4Io%#^-))0}ZS;_{5-?kpB>Uonzq(P|`!(x~>3oT3Sfx8$%buMt}#y zGh+46IjvjqCh>q2UEyW>bW(u03aLBUWp}Jb=@#}iBLh@y4N+_%(eIiHS4Hz-dagvT zsgO6k`ogSlSg9+|k+vL*PSsMUXb$tew}3Z!@I%Un^cp`{H6F)%@b6tjbiaF7L>+%) zv&S*X;?RDREuK66%z4uo+Cz2Fi`w0{`=*Z&Z4Z8!?mB$cju%#p2)#vS`N2W4dvuW` zuC%Kvuiaz=ai;`@vnNP-F8Fsvz9-l00Q}dXauUf@GI&bL&zwssX?O1!@;JmX3R>?7 z4GP>^5en2(4h|(XHEOk{&AL4~)dFqT@fqo70;MFmxGC$-J2sD_0^x}G2zFT!;?oll&$fMgTrW=p9@YG zuc?QXS61#p8xWg>g$sbCqW>6-;4dWXy>SL7J?PdpR@rQFy=I<~lW$Ex(`4alVOZUkQ5UY=kc!$)2X)F1FOjzv1=apY znGVTm^8Sn<6{!BEdZR{ID%K|~ab1KOAWMpN3 z5y*NB-8yNNM) zOd@?_Sn|c7yrRwcpz&;Ks|pHOqz4B(1Zk43u9_nmX|>?8;N^)tQrf{A0%XVC!fiT_>c0jJ ztWftqlrS5G6rjuJ!taYNddzDGf8xJSM7Il= z`Pdxn%TvxXva+%ve7{K?oY)9>9B7#8_aohBz`$v-;ZOS;rcn18GMC?1k-}U^=$-ov z;%Eq6YJx-0U*K$%EM^Uc$uL<25vCu|02njYTb(}K@}k}zd~w}nzwb>!W5Z<%>K+Zh z3>iyOYe;Z)@kSfz1ZeqFA#T{Ph7qerINS_a55lZX+Y!$>*H{e1=FR`0))RtW*e8^u zZr-hhWVg*(cV^vVxW!#c!Q3yz$+ z6S?s*{O1Ks;SP84p8?rN+i^$*vB2JS3U&Os=fc;{=j2*H{ZioY$D^=qa9=00pAqrg zPbb>}yVcVwV`PafG+L+`3r=4~!?NkAI~=#zwYRskbAf~+l&&3Ns6i7|qrv?|GXFv7 z1qM=>Hb%A z>MIvpmon$Oaq-$V?|cN$Qw#L4epBC$ppZmMc4PEY$%}+G>tpN2zsJ|>52(XT_@o?_ z*#jntX!?B3n&aUWurC1Fyp zvOv%Sfl|hoWGid0G%eRe{MZn#yQsMMEofoWj%eH8frcmE-`JZ% z*r#uwkvAb-F6gNsOn8vHnS^yPY14A&Z_Pmf`EB9XIJ4hv%gS?HS92U7_QrNGmI4R- zPDa+z%(wF!saf1fQpu;IKLgIj!>5n$c3e<;TGl7ujJ^5&h9QMco%N%KEN;h^1=fw9 zL}i2J&b_VY4&K0dE03r`)jDrIh9(};pdB-gL&V4KD&O`d-=~m_M2y3UI4t#{byikU z!HTGtJyCB0>54@HN#xN)J!%e&DzZulq)w5^%8_hgs4Uw~Co9JxW2$*`TloD*9*}hB zTvjr1$G!d%@infmYgvV3xZvq}-ym2bZtv0bZ1bx?SHwOkX6_ygD#LRpJflrnzr105 zqYb4XKi?QeONbUsXQvdz>?$xvLxgZ3?Zc7%0T1oXGc7G5L=hF4Q$gLGk)Iy|SqOMk z|ALGb+W-0)!8EtMdtbx*qdaj(Q!SL68&Zq1sm1%5Fw!mW+28zGXjzHP+nn$%~rk1pyd6|GGpA8U>auJ?W7&vPeVyB<+2qbRyvI8Uh;7#6Ypxf zey|G>CK?oTc`YYfU6JToy{GP~PxWHs#UssFxM&XR$sdpa0Hc!Z>iZqC|HMa#vXBN0xf593oKh1(IU62pG*eyz2h$FcHG{hj)Z zCBM}Na_0-WhLsa7o)U3!aiA#lqo>Cdde9$8`9DE-2^`z;SJKd6t#C6iFhCBaq^|x0 z@=EZ9C&#`GcG)}|a5$II#JP~oN5X^WeDiCZgtZT#&Tx>&k{)Educ{wBf5%Wnb2 zWRY|72L4tk#TV*Q`&8>gTbZ&`+>5VD1T`?92&anrbEZRj^VBM6?EBo$*01zh?btox z5)ZPZ;cD&fEx>Hy#fujr%=kvoQ?pZm-ux@D!TBC$C4}8g3wmCVWflW81WD6C5vN?@Tt*oh(iAu*kHr%F0E9~#e=O%cA2J7Kj~dw0v9E>;!wk-x0F-srq-PV%#+|x(djjV$0z$LHa+ek};#`?7_?0|zCj0De?LFCh zrBbex!IY+?CRtrkw^vgUNiJk~jl~E^cEZ6=V{<3@vRxxP%>sQWmchr6s{o?ww_ZLm@RvPAyccKvu?{Rcc88hiE ziGzvdF*P=yblWG3yMJ~wEiTGi|4GV`K&18BD5Co43*Lqq9G(V(na{(GC1n?hU(+{BpX=H}V?`I!|Jv5-9G=H=yNXNQL=LU+R8 zojHt`;Q@VbYZI&5hwa#0{N5%l?&B}>3d=wc(|P5z`LuUDw@p8sYm1K>Za1E*@7r=; zw{G4Iw57+A-X*LgGajZ!OLC8Roe5uAtermzC0R4tHR2t}n)uPN^SmsvT(SI|YJ9@YDK9JgVM;+yN4L-^1gkf4aE$c{Ev+ObU1;40 zzzuDaLYOCWaB%qXO=Owtg^rG_l~w*^iFA%KK#w1j=?PzbE+-d?|4117Mk*Q3>~r72 zA<<%sxOY>vygjO9eE5D+cwh*}BByY>MB7qfEB{{UXe;e3YSFEG|YBO3+kRSbOQXJvJ8FeLUYwHOxn_V-heQU5ey z122!=ofebQtDjZ#K$ri~qenhqG=1WNa6W+m%9F&{?Dhzjxx<6Ia{G60VTl#HRlZB3 z;6FQDp`q=YccANg<-PEy2A)Fe`@7a9C45GvwSi$+&ue}Nlg&BM<$N7fQcO)RQ_MZJ-50XX~?a;`@ z$r`82Xx$%X$dJzdy>SPoG{{T1K~^FI&1s0I=NLoDPV%2uK*L2Vk-a%K^%aBh^g&%*I49{YLbvR4v96nXC7zlP4KX(NJlwX-C=L(MW!hobOsO(dAfy zuA(m-^Rb1V6%M1a^77d|;J{csC4`<67!|}^4_0siOKAYdUxWT4^w{6C<$2rqLxl*4 z*U~W=NS91XH89B`6uz>uiXmHDbt6P07Jm0~*$JKnzP1{ar{RG%X(`2;D$#Kk@Uj(k z3o_G$r8VM1hgI3HC@KmmDY+XNxd54k^;K``8W4^gcv!R~^i0chX)K_8B2TWVp)t2$ zhyB%o{#NWBCEI|sQw7oAbtYQvV{uH3V@yc`{VF%E7wa!R8m2V6w7ZmjOHL_H<5aQ8 zb7lQ2*}(466_U?sr#Ik%pOL(Q@>`jQwlBt4)~ZIn5v#B{Sdo?WC;tb?S9m1ba~D+j zRL*eidH(FbitiRB4Fg*+6W9W9wnM8E%3atC2GSN5d3+Z)9g^ci0t30tQ61@5R#{ly zG}=_`y6{23fbQMm;`Y`+MDUnpY^}PlZ;dHQgKq=iTP1gm0m0pQ(JvzpkL5krUAaW6 zPYFZHs^%?p#i^O~^`+&0N~j$j0{sIX85zftk~FVZDu*B&yiuy^unYO0V=PuAPQtkfSlO=WI$W_U|%Klvi|u06t! zo;BUp$AZHoY&CXPnP<#k@*C6>6`v}Ua`K2R}HmQG`bs0p|*!&=S-KOEGrN%hKcsvusR9S?-K`5)toU7L5Qn34` zes^r@@X!f5U?e0YyP%0F?B>Rce0lJUye=Gqd=S+^d5hq2)Jk@H*?1{E zF77F2Si8?JlNRer7^p!T4opE|>Y$p(zFypW&^wQuS323B?PUUB>NPhv2SOE8FkFED zIN9B%8lW5A0=0N+mQw5Pd4I?AseS;aPIg%k`UsH<}MWllX8!r_EPmhnU zprU%t&7D(H64#KDksWW%Q9n%Gb8>LFQhzaN88Ul7S2U)q{)BFPVhHLZ$W8{P3pRHf zV(+u3MpdKTq@+3lP6xajYKD9^VnXK6rJ2&=(@s(-?k#fja)y^;|Y&14EUX!>GodVah0O23Kx+kv|lWJ^Dl?PHGOb}PUUm0*$7PZcL- zv;pYseZ#|#1MpL+lylZ{De!YKoJ8}6a7ai%mYSM}hcHPlKrXArh}D1@ACRK}SmFKw za@a7Wfc|*jiUUlvee1=Lb{nDFiO${b=$2W?xb-V1z?8&=j}%tFbtO^dM97qN`-;Py zQ>`r*F_^YMBw&m~ubpBP>@hFzJR%`ryaFjLH}M)BGrFINxc?nUmp;mTxn0KwnSr;> z)4OuFZ}{}~rwOb-@r>54wzg6+C??Le)Otd9?cMg(t1+GAkE5Q;$h=*Cg__RtwgcCb zfQUX-=_U)IygLC-&6dRDC|@Pl{l6RuK?MRKs%xoeRTKG5Ybwdx{j&;&mGrh;%A*@e zBUT)EPPJ$W`Foy)N6YW-&d*=B3%^$*BeRa-tn`An<$X7KbC{-wogRtgL&Y?DgE)f+ zwRdXAtjFbQb4k*Tj6T?7ydZvozlPt1KZ5^MNOolHS=w{G=D0q+67Dx_kXG(TChw6f z*%xmZG8XF!r!`*6)m8{N@i&rfyFsXxALj3HSvr0X1~9g_DzWO5j2*j<4cf7CKICRd z=sowV@J7?Q>4xF9tl;?hU&VxTU;Tqa*mI_pN1C3qgT(Yzw^bx~d3bDUr*;Xp6pRPq zdE0Q=RBPMa+{m3rowdb)>*$)SMb4QAqFmJVFNG9GS<} zw4UJeH2RUG{S(@uQP%sRwS!krb;z6lR9J$DyT6&1qyjdE!S_~Wrob)w&`_v?O9*b=N0q~D)Sbk>V^n&e~{(& z2GAX;=AlU{=~vP(b~S*N%KIR+&N#98RGUnDRDIT)?*mb>bPNqC4PRoF)2+}wU9rZ& z$*fUwB6Qw`>e-3V$xu;?SxT&pr=7K%JG3~oc%gPTOrs+|BsazvR!4Ruc0U}8DGE&9B)-KFr> z-sM?_YTn{D748MGr&i%eKzYVp9WRJuHy$S5ruKag!Jdm)>fH0D+)`>+c#9UF?+u?M z2Fe;kW(l1NF=qU0=SMdlDk|de=qNM{nrC_nwuyz4$BawCBXX)JVBY+q`yz}k+tAnT z#(d)ZLK8)+*FL!yR7ZufdV8)vmn=i4KS1)BW=~PdYhoXNz>0JJ*Oc;rTu4{`O%h%X z{YUeZ&KHjIcZuHf@1VQA)tg-kedyeqVK(8rkq8;5 zLTOFB$En9U$GSOUyW@-6Mzu5)i`kIVOAj;1*vROMZlFbfW83OCQcY<2!hR>j^s4x+`{_FA zXR_Ip|1N?&vGzIF!`n;b5S|EBI!Nb9od}#dVzi7EIuy1Pstt+Eb!HXc9J8KE3w$^W zhj;lhQbv2smTE-3z#!T}Im3DBBRoOrTTd2!s<2$X&axC()G|^mj#`cWA`#it|Kj-A zi|aVR8kHv0=*1Y#K&)SRQTCuJ4yB8-e#Cso!)2H@i7&r_S=7&3B`1#VuvmE1Q3b9tKwxZ16Ca>ELt3 zit25P%@Oe>T|9M8*!p9=H+7d*gyu6ovqYOsh7E9I_QS`aKSjm#!ZDgR(V~T@TBcg!P2=0Kcs#L-&G@k@ zAKTvXlDT{oHX=w`%tDh}K6LKB-E&o4vDEwS=H0ur?K{VKo|w-`Zd(`>|DTfDCwxWQ zCI>&%3IF?13jKSO^o`B(IR(-AZTXf#W6z5l@Kc&^Jxddepgp98-Xld%O#6Qhy}~$K z<4!4E8PA9%i{!A;C#3hw{a|xRylZ$nGkh7#yqVc6zG4P_bNc>gRP!JECZy~y zB$S!y`djb+XZf67g~;#Sn@&k7@c!)WkYpb1P)u5sg9I8Xq~1cT%$@8(?xq4K1@2haBvsM!hJv#Pog*ENONX5tVYmu6)FW!V;fLd2$M=Zb!RmNXefZ$2=-b@v zTv$BnY9F*##7LCcMtQlO6yYxt4lQM+LLUuTy>C)(Q$t6MeR14Ql{)_q>;?(Q%Z3E- zk}~GqT8^TZr57|Pk*2GCXq1-P{`V*30)Al=0h4m2U*aq2rkio!PmbcJw7YlrzmpEc ze7+YjE*QSm8K$mL(=zLm;~)F4--529(2BhkzTMQ!kCsn)4gEj+#y`&TdV_p) zlCQJGHnoj?*RP*uDI%F1b;4RX`IHh0$t09ziA#K8$4zErRYq2;90gs~p~cHkEL1u7T-xR>*{tVdW3{SkPplmx8Pkkc_KWv9#} zB^@($eZ|!LD_nF%uw9%ruUlJlS|jm(+vV@PwncS?*3p*oP1IevX5MqE1E%jc=Z;fO zFAah`q@H&T4*WE?ADubcCeRPHGql9bnc;0$u1pJkY4a!bMW zlB(RA$uU=~+>R@2+!mU=r9ExS9mdWlR(JVRD`@|LILdv<;)-*>8oX5h;2diD_2t)( z!^;1zWcJBJB#NDWFmYrnq-<<%#JOnI-_rLS+NS?9o|U}qm}PfZpLdmjUkYo_?hi{p zKkM8SFXoZx&j)<*7nHi0amd`R&F&Ys#@#J*jNslU4^cXrY>u{z8f`16iEai-Jka{D zJHCnR=hU8@z}#6xdj@iO^z{eZHuEhDRrYdbByf%YN&FnReZIp!b0TuGKkJ=vt@xt> znPk6+o73vL6mL(KKo4?>Hb*w3W-h+A-L?^os^1;ir6p1uuv3}?X!R*ZqUfRYTEZ8H zGpt|qzjj-w2(rWXH6}tPG?E(HNldd#A9$7KXS2wOE%ej3jOm8%SHh9>uVCR*Yw-OKXIo(~DT_2y)TifyCsTd!wf;S8MU8 znVf(^^Gn0eREHwH%F7IscLZC+@-pO)|NEY~sQ-Pa=eJCU9jSi$uTr|AU#O#~S5wIU zbwEzsc4u6`st=6+2U8le_y$|9Jb1SkYwPO8oqTfQwnjsvOk1Yp9fd9YPZAr&ymC$8oYU)?EBYU zk(r0LO*am*rT@x>ldsK<6!Teei*@{XjdgLDVRFEwQAk+NT~3Wn z0?r~ES{V(=~&TIobFA{eIuLoRuI1@RQPE=`a zV%jZ4mqU0S6gnWV+6w;2e5adg<$A4uLyl7VEy7?Ss||emQ@vM{S@%$7oGw}3H94f1sAJd2 zR(y{{yU*6|m1LjnIvp>hg04-!ru?VWRfdaJWZHS#**aKOqHj%TCs!oGfuMM6Xws@7 zVM5cIcfLvF-xf?2{LgEdJ{>(c_iWl?xhcb^A-J$e@!wd0Bg*-t`5u>~yhzm@yWK9! z@kZ4848e3q5(0Hxe6*tMCpswiA5A^#adCsbGZMDkcD5q^NYA(D3z`^AH0zQP&09Lx z4deYwP}@2fp0ri95j)F-TiAo2!%LN$Dn@VETuU}3;g9!+H~*8*J&%TDK5A}{)}}go zbc0Tt@g`{)PToPUW37(gK+g9K)!#NWs3>@&mPw`-I*e2TDc;XM@3CA75D_J}c~$=q zzBz1Z)T4j|lj>blFomBZ2W|!iMJmvJGg{S- z(`8ESyV1Ae4WkAY7MwTrzN+*FtdjT`SNBiJ;lDZtU3KzeYM%J?+W+6nq7SEAS}Lk0 zm?<4CAz1cS6XjFt{oU5I@IaDYsx1ePWle}f;*i%l&P|if8GN_kE|jm)OzGGAvg(pP zWfLNj4bZKnY)xsF&F5rkv=VsSF_?e$h}ckVoc1ZPxz1g4?)13*4>n=pT58ZAiLS-q zj*He)lTWq_vt?+1ZQgG^HlRd!T`D~_wL>E z=`f0X9l+>VvuW{;?s@WU>h6ae324EhenW%}qn5LkygbncVM#RD3N$?QM?6Wsx@6y9 zt)E|g!;0rw{;8|ny>wng)E3Xx*5jaiED<(RG&pFZC*2au( zNF~Zqd0y_&H?Xi=#Xt%C__2v4Hk~XuDUMO0{ZmORjku}J-QYxm8pFKTz)Vk0GuH} z^LE2pL8im-C2ylwDp^VZfVzPRTU0`P@`w}HTDt(}4wdr|}a#YMZm>1jDw zPm#D6g5Vhy1YSd+!2sRa|B_UJp(UtXMloSA1p`Pzk3td$+P|>!pxqA;eE33BOAGq` zF(UKO9EPqKW#RG9DJ3djkjhy*g7~$Hiyr zM{f1H&@&?qyA{?xV5~3)?e=Gj>W*UNnzBIUMNFJw7zYBQ0R4r|8H^pkT83suPF5Dr zK#UApo1QuI>17tY`@w`MOm(dJ3O0#Bd#Pyq5e(NI zYP#>=f>ltaiJE^{CV1_yCQVb(M2riclr|$3ht`r~Wi8upTb>vle}Modaerbds_*S* zfni5c;n?J~9ZWNFGW$^<@sGD&f+O+Yv>34m4V>;hK0c(+10x$%FJlZg4YJCUAqzz> zeNa+k#|~YFn(Aok_S_Ps0tT$L`#7sS*!SK)oQNVBW>|1}p*OC~F&*laz#eD;*`5tF z96TpaUqb)pa91~J;wv5fP5*0X`!^vD!~a&u$^V|U?Rer&B}GYTqG7so{H;9@D{)-V zDy&@ufN<>W?1RI@ozn0n{AFjXo3DyIIe=lpr~*+;fl&?^W5bNnuFDr8kCA=#K%kO_ z#XlH$$0sJtEG*KCixn|-2#JV5#`N3P(e=+tIbd)^4YI*70_~9g&qVaOXdt5^q*aM9 z^Fh89=0@;i0=n!^pZ1Q9O4!?%y4QJk_p&oG$`!bS5bS?=K{z1qAYRUKyZmYItJ9Rn?XM88v`l5t-AJxaKD; zpV>)tW@t}i83{atf%0N`SM?94KC}F#wsj(cb%LK{YZs%xsQ5hor((cU@yiHjmV9gn zG){G-doYqj#4-I&Z`Raf9V(v!lN3)fsAn}sE5gYtF0s>%KKxEjO0e#(iRbs{zrlUc z_`*>kM3Hq@p+!V{$a6?B?3RxZCMll;AXhRoGoxc;l>kxT=*S*P2_$h4K0?iN!GaMr zj37bG0EpGkQ@NbKu--xODJv@jc-wOwoh0H>-)5rHF$@Qk2Vgz+5bz41U;=)A(lEUT z=KhC&v)vS!=$vrbyXb4bcyVvI{~s=If38B{_W8GsCl8= zu7&y<$pRCkImGK`E_aS`1#;7KTdoDe(3muHsp>_-{gq%B-U};%ptIXlQl7mR*R5aG zB8{Ag=}dcX`Pr9t(+e*vK5<1}yD8)iJ*)SkH-ldt7rX<5K;UfAFfz8y+I&`^!3PkO zx-k#vx4{-687dd%;;5Ve{N$aOm>A@3>FUavnb95`9Nh2`0wX$LWFwk-DJea`w1TG^ znP!okrdtI;u0`hgx_oTOh-qD zAwU8~Z%wJc5d#naWC8wDOim6Hc*1yImDUu^futrJdB|{4BWJPcDnogB(!1v#9=N)% zmk8*RQ>L-rK~ zI-iWplf@##ZG9#U4eOFpehlJek(~9*Z>|4(ODk)C98=|1TxeA^HKxF~$-`=X<>utO z3OBNYawA)U(5!B@p3bFz;rO+)z}&?9)+*~xtNF-!|zYW04S-^4(+>9u6w4wL$o>Ed-RTc14kTXOOKZld>N)iJ({O^Ug zToB=4?M6jL;><>(XLT;xDFSKLcsxcI1Yoh zoX>6Q8cC0O|6Do-JiH{K=c1xZhNX_GSLWxgVUVn%^1dYD@kxqa5d1cNGxcoDGPg*T zx4fLt+#)?Yr?0=aH~zxBTnJzQ@VyUtCuCJ)0}>Ovdy1uo8ij*7ab^(wL-m9^C$#Sb zGDoI4j|J+`n7nwmD@o~ews`E!2ZTLR`L<0;1mhiE^H^slTpz!FMcceSI&Y%TB-ewk z^D};aId8s{ZfnpW-t(L3x3Rg*+c4`z!g7WDY6nBInti&{US>PdJW3O1X0#%HaZ8>` zC64eLKlW#~B0$wGfR@5P!T#}bbWpg5;XeZ2_eN*@4gZj{tDNWGNNr?I_yxeU5@7El z8-tDxNNg}dBM@+RQI{@I5AqW&l(h+7XY$L!Z-r z*<0D8lr#kRl;uQ42!M%WK?Udcj?0f1UOVNSZNdMC#?Y&o@5|uYwN<5rFyHQ`;gf#Be;%_GhK|kqsu;B^(eo| zP+;^T!lX`$D7M*HV&8h&lQIm7$x40F&AdsF4>5%3RHA_gZQNgK&J$7Wkv$(1BF ziHu{VkH0@QDW45?#|zhKq#3uGje>R(;8}mewPpdf1b7^b_4`VU4abJSJfdz34iFd)bHa#C z>;wKc1PfYaI}h${X}2Hwk}9gHA)s~yVhrmFJZumNAI7?EVW8H013FxH$9?W42d=vq z!jgf>muG_a0C6^2+Wvm3fRclw>3B!(v5XC~XPr+SpL64y9@FgzRh3NRCAsUhP6T7s z0}>wfC?Su3WGgbZg*h`D2l=4hBfgtJYqZjo>5g-TTBz0_SHOGZ+jB|r2mQV&qlrid ziqg3uGyN6RKuC4magI=l3^{spty^gb09El-irzYth)Rl;KOVyw3k#i5$$jaM+cBcA z17b!A0M^%M#y_6L^7;Y%-PW;4`_TrEGXz^r=5@{j$P{VDYF~uU2@*UA;J91L(b3U; zzfar}ls4w(3_&Xkc#tn)%K-;o*7vS(Av%f=E*>6Yx&l;9Fs0^3sD)rW2x?9&AkSg{ z<*c3ifn1fgRE0JB&X8K4BJkwE_G5e6Z3}Ut0wPiMLZtLZFb@NITf;{brR5iZbX@ps2m(Nq zR8%ZL#`ps=LO?`(q*F)gDCc|cesEBaK=5K0)63&c-bc`>=}XsMv@R`neJ z;qCgb4FW+7S~8Z?#4VvDNi$uF>Dqv6oe!cPC*T{Mm^Xg)qS98U+wp^A=wkdQQ@P{w z05;Muk^|rKR0hf7l>XVn+ag8d%&?v1gO3^FeG}nqT}L#A4Mo|vT$$GniGi7`XD5s?a(J`Xk(D7 z2XMl9;BjDqjE4Yh$q!)a3S<+T#Td2Zm??qoahv8Xwe2Cb$J1Vd$qZWHE9@JV@H5$})o8W)oeAJe?Ayb6UFt4uVM5?G zx#UT@$9-fv-P0BsLU|VD_EpA|sIvbT$2g5q+Xn@d1u1W`s<1SegPJ97D2Hc-?qF(D~;Wf4Y)|sDX%+c|?P5s>B0n?9)2f zXBR*UE`>dXcshFDME?lpjraqi%aw2j0y7xVk8%ed84y`)CafwEjSs*JBkT$LVZVEL zc$%R9_AX2yD6?MYSB4~JK}~1#R}Izu(;ryhsc$#$NJe_UZeS^~*5OOwXuTq2(p|>t z<;fdGJ>h1QK(C~>t=g7%+Yq)7q7e+Mkd?a)LwiXQe;PA5Kv^LGM zrB;oJsd`3ykK|tb!pH9?p?e)prUPHiVq1b>gmvb42J;F4)V`u(0PrMi!HBhvjXH;= z$YpuHjS^WR?mKy%K@{1)-*g>7yY(zuOQ}km@t;pOtP~LOX?^XvN7Sbz%=Hq1PLGus zLm{vmm2!^g>xes`;`lN`zO>sf%>;2pj|DuK5or|0>IIxWb}6pB!I6Da8Q!4i(AEF+Gh{1x z6D;L9TnqTbYQ&Rm+QOL(8-6$ZJX@Gp{Y(hu)#UEWP4fnHA0w{WhQYzXB9|RQ01|?5 z8)C_uHdfSYMJYK46pcS1S@aS_CJ;+=U_Of>dypE8+!2rD3y=>(DkmN1sa80>D0H3( zEh~b8Il?%soPHAwwbJ&)%tb8Z2(WieGiCzr`T4!Mn7Nb&=C77-}Y3^Gr)%mTvqkJT|?Gc78LKv?z@r=$8+d##I*o> zS;o@RG9%bg^VEvVpfm@59CzBCEut?y3Fuw2iCzu!4#E9ah_NstiHC?0K(g`1sul_! zgcXU{M%JBe)CKDCqCZYWYs=(KF7ub zOx1PsyG89Ipg#mUQQazPI;}6m_#c0oK<=j!TsXrY7ZQXZS;Ov3Ai4v>2T z6DfgN``5J_9FXvel4B#<49l#Eh;#2)k%7pl$3NWKciO3ArfXNA{Y{C0s5ZNXO1q zOjysyHJ%%XZP-&1dsuvv;BniyFz-fy*5H z!;)jJ%Ed5%>;X_%0>>;d-Ok~cA^K6f8{kf^Ur9!&w~kOffhGIbZC%}SCs?ltWILgh z1OcNl$$%TCZ-@n_Hk`65DmY@FXb3~6#p`Thh6@<9Z|X5j(`Nkck$K|78o^pf3ts0t zF(T>fOV1=H4@+p%&^k9Ix^dzxs=8RkWK=@S>F_h+DAK8zcG?tkMo z?*HVVaR}yc)G}4ynaPw<6HEVc_ll1Z`9zd#0dE|{TW;V$@^NPz^VBYWmC@)3re&hA zwexwL=S@fH_x-1Tyk$alNPLS4xH*=PPE<-Pua1(3X13qYd1=OszkoP5!b&v&nMq3O zU=3g2i`iekoH@NJHpcBS9~?i3c61zpJE;#sa7Bs?@CA+p4+7FBD?5SnbHyaC+88K4 zNLdR-TNJcLiryTorqiWHQtkkWe|OTpXQ%P8Pv0Y;HAU@%`dRA>EocG~ zKwh7<|0(K3qpDlzo}|ZqfxvfevER~& zDGRFgpKSd(%T{f(;5QlRhz`Y0q672Qq$$xW9A934FYXfRmzH$Fn@%1tfB(PUOO9~r zZ*Wv^di8X7N$|3Iq>|}&R-VYzx5j%HzZgVb&qm&jL*O0?Y4qGVBZwNr0#wCIaA*d< zV!jHSiBFvL0YnerrJ;oCTvefqK{UhQAWc?ezM7>=_J45o)=^#V%lo&5ibzOEhX{y( zfOLw2bW1ma0wOJ)N_R=8fV6;sbV-ABOLs}9G{3ps=ljR=thHTR&tc<-`+mpFHP^gm zEq~r#CnjGRhT?Xz_8=Y6ux;GOH-fFh9Hk4qaBOVkf!_Drthz9WT9T;B6stN3izusQa@i~WBA-}{1^D8TBq_q;PQf@XL!UBGrBaMOyJ;%iyoLwLMb%&T{9!bz;RyU-5XxUZN3 z>ApYP&lGqI?Ub6%JI!aXKB6(F0A%zQ5nX!cFcd>De8W zH7M8;x4(uaN&R}GZm@ZF5tE4a=?W_0tC{K~!=z5B%?YQa?#@K+G^LiIe?QMYBssYfyiGYAD1m;#WD?7`6B zr+Q7d^Q$&_9Fh~)T%w|*TcHKM9cV-U$VkA_f!4#WspO5<>%Y~L+-gHH1@0O)-c-mP zPLJN7P_o1)_i}p~S((%RHiEVyWl5Vz_1^z^0d5X^etRdMscfVnQWkb~_^MQHT#J5l z$WFJP-O)~O$l0S)0S4aMK!L~$zn4ywk$-;O&*N+&ZLmQZNhw#TZ`9%ItWo-lJ+o>* zV5hZG?3_Q}dR*(#ElMJ(QCfEf@R&VlRz`FW&~3&{s@!|kYv&CjEB!GW1ZJX|G|E%{ zqMxz;NnUuj7v~IR&Lv=JK0@KT@13SCIF5DL-sn9K>2`bjpU`UT?(C#k=9CtasbiF72p{n zuoTceiXfnd`n_tg0Xkevk2<2641rp=Tj_SZh>t)Hkv%3WaNdN3OVEZN1N=dbrZ_mF3x-<&Dh-Qsrn&&Z6ZnOMnEwa@`CLk^Z#u;F;*FbYRU+! zZ04k1svxfWv#{)(+>dWxa@5`$2(90=RdWnNUF)q4q|kf;qN zH{=8Y=$xs#uLjmx_KVrBu_w1_XY$$rch45)Cyt|bzao(3Ae#pT31$Fd3o>M>ih_m| z2|m+BITxrMNF14ylcNfB4Y%VCog;_~p;rLj1plsI2kcc=5G|364}vu0nO?#XL!7s! zccP8|N1x5GY6GAj{(v1ybh|vA7VD3+WXY3u#~}koxeIgIq_UUDz%Ol4VgUC{xcr@b z`SN8ikPiWz;X7%z3>XfSYW*Bg$EAVw3*r<|toN%8`4VIvF;#D0LlEjvynC5DWVM!8 zhD|~e*k$nk$2zIq#0wCcQPI&CE7$THO*j;kYz_%^#lvao>E~g&MP?>&yG;BT-!SM) z6=qQV{*jE|5sQfE7p#k5Dsd#_XHYBh1oh)Oz^vbZ5dfI(b7ghsPr|8?VJqw-njSKl z^DV^YH>&%Tw}bJWH!GGA<}QfcO}fm?u4nd0E{hz`fE>CFp8_tkD@N9(vMF-V2o}p@ zLl;h!2#4PyZyP=?E?cfCz9T=3X(8aTHcB&vX_%aq1?;C=f(2cGp@s8t;_6SJql(MO zv>36mfb5NQaDFp6kt%GQodFTbdTkgzPZ{PSc$7m(7GFa?J|0Mj2XjakdsBw-lKRDY z3=}(O26|-|vuS%GR>a;|oa;Wji!c0?m({1?lknS$87t!ub;g|R&(<+4($*hr<~=W7 z`gA72f;|>&4H<@2beIC2-fTu#r-vKww=!-E=2qZMVK~-uB#)$O>q|{PJ!71;bH=*3 zDqwio3A9@trBC9PFoh5y(!{;be~}_*G41mA9x16bl-_0ec5cXIDs zq45T$;`Q%VixNMHE%)#`QEzkEsc*wg&m7+c@=DuZY8sj%hrOlZD1H|r-WqL8d$+8l z{Gy_8$dcO#@!%jr#4Mu-cFTpfd$7Xe&O!kTawW4x=^Y!ZM(U<`tl^ zhDAr)DK9tyM<1bloPpeUc34(N{q$)V68FJ}$dF0AW3N!{9BCLo(6Y3>aZ5ZrZm1MK z2wE*!<`zyetImADq}vk*Q<>?Et&WMZBv$CW1>wH}R$RqK>G%;u)~#Evf2(MsH+->i zNNFRdUhB5E=kaPwvC7VOWpN(UED&cXNx!#>omRO>I_6*cAn4?t6r7Ydmel*q|C;}H zFV{-mZWahXVq!*oM2_Q5tUq}^x^iqqq+14a!IbQ7q-rilxKll<8P7gTxzPCc;5BUpWD07%@XTGE z+ae9(VoXXkS}H#qhz{`cdy%ckVY~-gP7uI1;kA-HbB=+xlRPv98Z}4dWPk05*P+md zvhgWBef_JnPo8WjdOc-Qj=%B9-F+fUkt`=??9GR0DI=pSzNBy8z6E9`Ir>AD49;vn z^}J{1rnhx)i?Z+f2HYbf>xL@I@!_~RLirzsD^Sg_&1D=&gV!Nnl+It?zb-6^i|3Ge z&RhIk5-@cSO@M!EsJl2|0u_oMxjfF{uIO8PFR7$NM};aVq@lwwvcU1Q)_{-P$HlpO zrm3W4q`zx4D5UY((#~9~MvZjJ2HwpCBI2Cgtd+&}=8q>=zmC0HHCz)7e~20>YBM~4 z_oh(%FkU&q@(IJ~TKOohC4;E|l*#v<^BxtG$4yR2-}6Kp(;fTiIBjTo1D@(^m#;9>pdAaSwYCpt(JkL91w)g^tCe+twM{%XU7pUc-UJw1>PpRrVWzA1}L@SXeM$0Sxvm?Cc#$CtzzrNK3`U6arGrHbVRfaXS-wHglBG zrv`-y*k0Qm94>$yy1H=v*D4G;Ct19HbWq?X4Q3{U>;;A;}z_) z%Ft&S`nrp2?NTBFxhCV~&37}41YTPcpBHJRY}}~uu&+L{d}W%wUwH7&_T2aDS1RCt zF7naN%szXV`SY~Tp@wvfjL+U@1ZNm84-F2&Q=~C; zh1?+dLn8jb-^yNBqi$PXcNn<@bl-xQY?JYI>xM}?mF@YE7bX+GxznDV{@u!~u1xx63SOfr|tnO}KDr!R1yDkr+_;ws7ZT z(9o`{Rm>Zp)&7e51jVU8?vD^>iVjLyBEicu~HP&$&XU^zn~8h|ACNckRAv z3}`6+wt}pH1lX?a@>f#OU%pU0vyLjlf)^Flgw1mAwGrjcL(}zLL_A@IejZ5AwL;$-^bvDqvV~ z!;}TnJ;APx^e%Ut5lw~GAMcc4s0TxpElTN-0h7d=H&kF+L-ZDKaaioH$X|d8$NKgc zE&{@b|MLn0Ifcnx09pM6XOwR7C;)y5cx~5170io@ijE*d{I1FX0Ror@8^ft3%Ymka zP+5>*6aH$Tb0`O2F`KR>6%U7?ZP$g@ZnJ5AM@~}{l8Y9-LW|u6@ro{l6AxCUZ!p&o zN`FWd43ub?%F?)MT?Ftq#GL@C)lnn)#ZQ=?0SsSV2nB*lS8c06*bk_dh8Ez~`PXa8 zTY)#>ll@$^#>T?>i3qS-TBITThXhbdLDDuzx3_b3&i&yp;26uSJMJ=vrenA$%H@^4 zlymvbiLU}61@3OBBtUiqe+`U0MOHtVaec z9M9h{lFWf}9z*%>TY$nzP|DhI776@ru^tlzSN#LN31JC|R)$b+Ac?~ve+;TJl1YtL z+R!7J4t!?e6c__{?-RtT!1FbLqXQX412AX+?ghe~0`N_M#2_yjSlG?5xCkJngQW;o zUJ%8+sl*ZX4CHqGRd(i}>OUmc1m+&d!1GWL=LI6y^%(%?FF=L-4e$!Vm<{zwa_8-< zaC#cTj)u`7`89{?!v#9B0Hi^H$xwjP2Rtqql)(HK0PXC6JOLdv7@SY6P>FLr+Yxm* zzvJcJBXFt7$W;t}9+?EL7l3skdB+a}eS9!T=ioY|f}jsB59F(n(F3qkc)@%xT zV^L2R$grUzLu^+5;u-WXZB1V$m^#D67X--%k#GK=Dv*Z|Ee$pS*=0x1|_!O<< zs&?@T&ZlHZHr_UPq6SSFf@Rdx%S&EMs|(3?k$f7ikBYr+-gvF1th~2xS3xf08Z^c< zud#t~jhEdCSZjfA9Tu$S5-j%%|Gu+|#nrfz+o-INo~`$C@^+n>&Poh0uxlI-fNiFe zu2E(ND8zlIw3_nC%{|ws9=o$EE1-C#={G`z#T1zXi7$u1b|zX zj@qK6u-o3;ge`oVGv0iuFI@E4jruYqf-1yTn9B{ZJHR1XFbEI%=}f9y9M zgKwu97#O$!*<2cE%Lq;!S)#~X8A$(-`6WQ13=kXlR6^kWhnMrGrtABJ+2 z5X>bqk^*Ab5c zUgs$~0@CxI{(k%%r87t!RjB|Kwc-C11ePH`07dR^d5SMOKEOQ_$w-y&y#iCue) zYP#2ETK*6pQ*TG_VT{LU^2e$gGBCAPJGYNLu;v4*vLWz}0r08PO@-rG`rT}j2NruP zLNp`%b|a*8_UAXvZe;zuue91UnCA^DX*KSEU)#UW%ta3{%n9rtZEc8a}_9 zEUCVYu|)tgS*WS0t9O6VL(L<<@=p#1?_G(NGq@Dt!@Py_0~sO#8A{H8lpWpe3tSSC zP8dc36Cn(D=9)?P9U~wI#U}V&_KBH{lgM>CRcs~NRUm6>Y^KE7b^k*}Wx2&$ zRiIzO!Nvz3*+oR3gV7_d(=AXcj)kdyDAB~cwhZu5{HM#9eiXp@j}#P;dj=%PWszbP z)P_hCI6je5EL5)%Q-oQ~fwDD^mJ_F>kym9Uk2fay)JiQ}7R)w(25pVyTYmu74v92j z=O2L#*aP{~bX>Pf)%%U_!5H@vwzKnd<_mWL%btA&nPRNL?nKN^E4jFg4*`8C}0~O zuTpdGO#w1A6ayMSd-Rmagb_T15YLjP_5Gfb#<@`k*9|ea7dhbH`hfD|JN&H=)7=Vc z;)jf*l`I-W!*yFR9_qLfHo)?mFhd#9)@Y4+%6|4o<-Qcny(}!x;FeXz?;i{)rZ=sN zYUyr`M}4{*z{&FN6?>ZKNa|4Omc3ABLwnmoSSNjAt7&9eqTMLzC*GK$_xF45KkJYl z;w+Iz^9pX5wP+)4hdCxl#SXdL!u;ue#88QwOq7Qq1|A^PS;F87X8o4CY(&ydoYGi4 z3_v0G$D>E6`SNs;$hbQwEcQa~acCAt@xy;$^$jS;h}i%#W2D%M)Qq*22gG!u>iq_W zM(EYb7-I_RFkUaqY+17s5-Pi%uknZkf5QyX9B%>-mVl zd3>r$P2wf33a*Q(i+Jgy&nA*Iqe)UKhNseze~s4DB8(-}T%C_mqFtUY2`hW$%{5M7 ziWdc(;Had6wH7gj6v9v+Fk&IK4ICONtb?nbXjWB|&Hzq51K9|fM+I#9C?7IMUl3$`>w>_!3}IIboD<9Y_>KnidT)Wp-=PK+Ag zSVD|hppgIjH*~l`42Bd90INmubYEsA6bTj)HRWWjpdF@wz@_M(d`37V4|x-Vqb~&T16Oxx#6o3oUHYhP07J z5~@Tc5ln)$6vKoa9_g4YIktn`Wvj~N++-Hj^55q|H?cU<(IZ8LWesJyRl`=qs79kE z{@nEztrC!ktqimasT=>c81gQ}((o)%Q^^f4;LiOLac`gALN$i_fadRj;$eGd=U4D& zAsAw$WC2X%L$jpbo6yYyGt7{A1+en0AVq{d(gQQ~K%H@4p+FTR-cF320)Q~kr3K+Z zLh$d6LHY~sM(N~dE=|mQv*F-}N2auHjF%NV>={6L&)3iIHN*s(FjzH(JKVq>_ zOZzAcA|{;;-j%TYHW9-6tEJWrnyRoxfjgkv$b z%fR*G*bw;z^LRdun$U1@#Xxrqa=rrGRSM)4Cr*Wx1rYOp!=xavCL6-F0?+@8BHijp z0XdAD(1*tb;<4sELhNNqcbq438o88@TAd7j+qHKM&Fy?_U!ZwV|3c6`(euVFKR*mT z{lumg|JM%$mlv(++Ilc4I8{Vtf}`|Xe~|EI%pdJsp%VYpM;jKgfIfK4xU++CHk8cs zMc`+EmiC+E*W{ea({0zOF*UyrH&)=H;D+UO&}bw}moml+od`u_cU)N+>s4Z`9vvBu z#&xgbv{xy#?^NmGvUypl%8=^sogzkMTDGoP3ybyNPlL($ zf5G&{w$9E480Z2vM6i*=o_NU49tlo*aQr~JD(1vl(TH8|C;9c8RM*aY5#-HQNoMB>Tr;|y#`{g_Ha|}UTT>I`Uw1X^_#jCX3c-=r;8Vwu)Q_B)4m*&51*f$PmtRQOcGV@h)fYbA$mor z84EqPh)w`8En?t-0y_}Zm}5_6Q565r3m^q8n4dwqy+p^jU)=Bm4+1HO>|q3+08nw) zr|Xg-UVvT~=+#724M-6~5xyriOZtsjD~Kr(d@k~cK~4Vlje(Ai?#gWp3>qNH)>8dz z;uQmRk+)3U=m~Vo0b>{$b^}9jL8^giNrKSL1^taf#^Rs`Kz$olI()~~q1VC2UfZ=VT`NW`!xBTdWONR<)815n9XF9<3&jt8WT-+iNRFHzWPCRyKJAzOHR~J9 za2VrTYU;YGD}RxCe`o;fhE5Jr)<*`4!{%IATAJIjXO5Kua42;33F_%lLU|v~xi15& zV^yPxQ5}mQ+~B~MffyzPFj*+!&%-?n>l;dCi)>pF5xDRmP;cKh+A=_M?`?q-l*@5H z%lR8}#2|OK3CDF^mRMgP&N0FHY;LWwLI7_X0wgdTBb{%^z=m^ZnSrHz1->QHiF>$w*zu7*R28gy9mFm(; zk#W0LTbIN{GKnRa=r%ZbLmwB9j}sD#aBVz-kIr6eDshCbku(zG_8%18HiDys<_$Vi zbG2u8+I(z-A;Bvc$uquf)!E@pyoCd6?hu5!DoJAO(J=2z%1ZM*$^v6>2t6+J40Lm$Rx#iiB zizn*_WE=GciCDKjW5?jFqOu6{dpEO@VUHs9<38trz@`x>TNx{%?NV?i+g=ky%GOd=@h%` zTjluU_ak*h^*=t1)0-Q*t~&pCHO`kwVv6zkGfnY+(x`xp?Ek6?h{(tP~Km{!ovSGS<~ENxE8+1;!<*15*Cj}T%sx3d*CT4P0a|;cNgvk23rjXR7Xf)illkgQ=|+@ z+_))05HtmV4$t<5n*0HWg6jl#%?6Eojrdj$rcU8K)8b-bQKSqw$kA5+O=M*5?W%CS zd{@%X&H5a|#Djxnne>8O1h*@)l? zLtbNQ1ZiX|4Pn!wgd0ddgzNkH+Vh*km?qW+7uMl|NSrQk^Sn*=g+N$dj|lxc!dC*G zmhAK8`I|g}o83-VE*>1+x)?hsh?$^CqIV9y`(7m8e)$3Y2h)~h!tp0(xy=Xj%W$tx|b$%Vyz z@>H-7!EiDNZjqJf!BUI+en(#sl#d1|IU$Ro^CFhiY}zz;$Z>dHng&NCEeyt)28yIP zkK5_M5Ips~`+3R%|G`q23FLBzb_-$3gBWA=`sws4?j7OJC2T;^Ow%t2Y1Rv?(^$9V z1#2Z>Upf}UFI7}jd|KyIfJP7aZup$wksTZwlGo6npFTXd;UUHpR|cQZ%+mgI888_* zcm748{4v(;6256g<&M*!)_qqS76A!{`Td`(q}IS+X`HRm(pcMw;3bjx^Cx20k&n!R z3&5L+xmbbf8}+)|WPCv)sOkO0x_zsBH*Qv<-rc{c7k8%F9xWAHqOzgpdoD_+lc;!+-|G|hXd(4bR!?zQo0JXVDxBzBQiLosVt&xFnNjcdrUv&}xeBab7; zsU);>(xh+7xO;dkvijF{V#&uyVR?20HO~Hoy{)DO zvmTJjUzaM&zlImd8W#LEDd;vSTDxnBF@*mVEd9&FkN;$KB-_ z%rZXWUNPi|<0u*}C(P^@uVhbztw*54nnbIz9E;uI;e&UNUKO{-cQ$I4Pv?lQepB)< zHT45a{Vn2So5=!)>Z2Xyf&z*!AK!*`9MLV%N03p23n1W^J2o{~YQjz1zy4MbE2aKy zB1_Zugw73(v}g6xooPE~1;f6$lM8_l`F7K4{L|1WTFeSSp&OUGZ*w90hAI^-&)rLB zLiEfJ6`IlprfiW$IJkDkyFLwdvstkH5bBaz3|rac@B|x&BysA7>B>^n zhduXy;`LHARcHM1fAFu{c@NwH%XdXCgHhjnMjN=YMLpj`Otd)A{clQ{;eb5Bt=cy^ znh4>=wnN+Uo3-NA#g%5oJw+NNS;|41zFVIqjo%Hvn(}q@xFtcRYtSrJS1_Go!Cd(F zrN~gwoL}mrvs%}WIAb_xc^%V71v_8sZ4B)*i_+}naNbg+Kci9?BSo4xbx`KD~g(p8sb|F2|J{W{|9TeHlqY8uVC(x zx5v@7`?vOQ!D{`27`46>)G#S@Dj+8fmL25B(IE{2dat9s_|*2lPQRBk!JO#+rd_P6 z`aP7`*lc^PGX7Ow&Jvh%kEqUYQi)TK6;_jDrqUnLC(| z?>fglK@SogSTfZFUC~YC;`7L!&fil8vK||k;F9|H$7d_cu1vuWjMQ=1Q6h|QZB~ru z5_dWolG~f&{9Jp?w$Tr``Bgz0YRUfr58dxWOCr01?{M0|d*c7zkY~r|+kkep5EmEw zhNj&LCT5aeatgUSF9jPien2PNe#u|c!4%V6HL9e|L$+xYw^siNJgU*pXQkTl{VhM5 z-Zk;gA!k%wUKzKQH8+EwW<%wSP1E>%tim&&LUw(ocU`_2*Y5jR`+R?FDt(J#{k&HK z86dMh)Tz|yECZeZQ{thbOi&t1ePlQlIuW@Awv_0K;O6B!3g5KYC*mb`+u~O5& z5)U?;!9jv>MMgqotFj)r#7Z~+zZI8b)0(cxw658_tg@qLM^1C3JDrTlT}N>{S$sJh zqqSc{;noRD>k7Mm|Mc!@ma!}Z{`YnSTOr1P4_CorL)h^! zs+h%u8dM5{$ey{AxHaw^UWtqcpFK`INP0=X(0;l7F~2+i>zINp%9Hi;Gi<4>S1Oj0 zb5HJm$Cz0>i`J;A9jGDLqs(nk-g&+0t{CXKHR4$Sj z9K+u&y>oRkWAq9bt;9otNe_8 zjmtQWac|;=r+0)0WE-p&XDJM&9A=3ZJqk0HjP$rK9{iP~!v^J6wS4aOQNo7$N@2Gu%QwPI_xp#q*5RA(<2rD=N4};% z-d%})S#dV3 z)vcJp_#QWOO#in~UQVezdcddQPj|D(pI1M;2p=B&zfb1wmHvJ`=kP4L3&p1#NlRZ4$4%Q#jpCEL5q%x$_oa!#@ zn*3s3j|&&07X3I1Y@~<=E1)5OT#2acI6MpMMB##zoD^j{KQbyANPZ~{#3fx2J>tSX zV@b^UcMQ1q+9LO#Cq|~2LQ`sturv7?eo?ZCMq0w-pkq$( z;-zP%89H%pRL8|pFMPS6knzGvdqdTpG{{)|^M4l{zUU1Ev;3XqAeLdhC=^z=6o;&V z_9&mA7QWrj@KCN4{(5+A$5vu?x2yVtW%^;uWT-1igJhcSASqs$ab#2eGykX71il^l z$e>nZfESZ(`4}qRpF7WB3gl2q?&E&kr@cI(kCotz<*ii{(&xDr-O%j!5ocE}l>d_x zS3ZBR=gUxD-SI`_F0<%P6PkHrmP@zVE*PWsYcw)5us%Eaypy zu!1(MvE_^M)79bLAkN(>icY+xzsoaB(BWPnSUU&nyRC1j7eWfZA)JcB6A>`_Y)n=M zDCg!xUdM|)kzzrQRm`H7b5H~kFQYC`;N39W$@mG*^7iE(-xq=BIZuYR*8U@CuXO%f zVlvdS@vX&LNpD4GFgA0_15ST!zL&R{5j2q?#&MKIoOAFLuS<_`whh14R~d`HtIDQi zz1O+S{KyRBO296D-^kR6`9_^$k-YWq2=lLFZ|yEkg*tz*LHd!hdhoRQ%ubv*xou zVpwO@_IL1$@g_)m;yhdct*}A%(VS^hZ#Ke8me=_>*gcvaOg=D?&=|Oi=2R!5;1@S! zK(=|oe0p%|khAibMp<@Ws)I2P35EJR!lY{^awVOViesvn>C8&WnH7 zm9@HX9A(m+OqATOM_YL;o9VRG7^}LKRj}6I$L5t(Imn zz*QXTfmb`C%(S}tev7^?-{YId%AfVI1)dAS1KBA*XP=wG*RR~c1y8NR(gG{2I^&Ma z|52P%d}n|woH(|shKn4VQnReC?WwBw3j@m@P8-b61_9M4ci6_@r(WG_0fR_23{}sH z2Do&^VEf_U(b2UypHVKW%M%w{XSctOw;LzjjT!q*F6h6@T7Ebe&sbMmV_s#FBicq&6Q>SK(zLu-x2Z&v>ze(>( z#v{2B4U=v13RLE7lU>~8LW!FR?dT(Q*($Hf06m+z5uJhVD_IYw{5&wzPS>de>)dLTobY_ zD;y^!=+WV~Fky#s+%VUzdB+c_*~>~wLNmf{iw+BKHE*r`i0`C{aD{!Gy#mDaHnulK zI(6j5V}UJcDG-A~u(a^ak2RZ*4#My4^>#z?vWzmz4j5D-r=lVX{#+Q^1tXc@H_&*n zI}N@FA|fJ$W~<}V2(Hc0tzz{&@Z)|I8Id)Fxj#IKhLhx2SRZBV4g((>mk@c>_pVf$ zL#HvEB+%RlvpZpa5KKM>%%4b_eC-VslzRjOQ0EAB*05ZP2AvUB|Aq);{d+~@zy>tV z`lk4rBNzNm`W#M7Z{JSUL2pG)&fwrM2Y*SLGW|VVs(ZK#A_dX6??%XF6Wu}?G-f84 zxN*b2cdf?!afy0^F4yTs`jF%GyO|#l^L^eL zhjYM-XQi87Tf16Z{pk~SeLW*0;(Jw~qJ6kWxJt=3=;Rk@-TpULYV-bvd5R66z3RO} zcj3MO0(jJWUOf=W)UF(ySBxjO4;${ObsM^xELPy%!2J&SppKWbSAQDSPn_p@IrmZS z=p`0TJ@?ab1r1MM+^{6o6C&nT-q& zztYi}UT|b13Yv%o1eZ`3?$?Qe{vPnDe1Cy3h?5h;tn+ofnYORaSo1)4 zCpcx-ZL3H~c2M}7JCexW?C>8-iKN~8+z$PBH$WZq^!COkBBGndKtua%?&IUr(Ife9 zpIzeA`_acxy?B@HJ*S6GO?@>Y!ie^RenlkrKOJmRns^jCfc4&x2#RChsUH< zY5i*Kt*ciPSqFYb56yO6*)ecCGK=qmVpw&3>@SHD=bD^f@#u4DBa+q|W6z~Jvn8V$ zyDzou`bu(ff-*jACI_29c?r|bjeuH0yTd7H>Tn9bB+0~uql{+a*J;Jp=ht1^&>x>C zrO*PG(W2-;2y|xrgb*F~z5n9u_1cEOuzWFWFr#o`FF4+U3>QOdxjy4ZA*9aA@|DU@y2AQq@M869c@1cl<2wj?!U2vWb8C$evbl@-bii4 z6klvv^E_r`%m5!b;1VSyC3`Bh)zsQob^6rvq7a$9u@SltsTmjoK_*88bSV2VGh-q1 z%^o=8=N(S4_E_IXT6H*lVRT?=>3!IN|KT#NzGvj*NYrjva4pyXG8q0_UjV;>i5q-Q zSa^8C*4Av`+S_%Z{P-{JL87euD~pT9ySqOY7EwZzx6b3EGpPjxo|ADv2Zpb|ReR#{ zijj_J<);-R{Mp&5=bq|Mc>0&;cb+Rna>~i8=VbgCBwlz-*WWf05WvnwGWG3ldcV*} zKKlOp>Q_-rpEFLzZgKdd-feOE7$pO;KRa0Pye_o2?L-EH%Cin8qAZ9(NV$KXasLAv zlS$$`QYLGcPjHi5s+_oilma>#48D0d_Auy=D#-|$s9?hiNBDx6v7$Ze)V=A~Yy1%Y z7d4lAArcnagtze9l-%Nf5?15^9Ypc_*8BTw9MhO?$=OTUdA}se2`~@sL(yLlhvL2} zj#ByZ)5GUN^pthMf^5_+b-R`EWyz;L3{8F*YHgIIL_aDoGpA z-Cl-4*^9cuYX9M6p$4U`&3MZldWshx(7|YAFa-MtbhT$$hnwFqe@cuduh?yu@Udfe{h=l70>UyVz{BlS~e)w8a+ zQ1-Kmqn?RZHj)7&g^(}0g&gw=ke*~~8T3Q)TTQ&fxb?DDD3w3#k$Vz$8;`U2q%j{>VFmnZ*j|{0a)kNMR=&m5k)c-)!$sxta?Ez!=)_UT%)_L6 zR$qt{%U!7y;yGd5kW76bzq#Gg(t-^O)ZTuXVCn-mn0LRx?f`QK_s0mR9Kw5n5}(yg zNoZ+-dM_|_kh-UObN^OUTySxb&QK^YuXE)94{1{~cQ@Tlx`ZM& zfQqs#E_oxCq71lCmP&j z-KPZqB@#jI=A$J#|K|n38?7{fIUL3zaa@L)X?FwOI9cTy((F8nZlLaQFYNdpQ1i*@ zX>R8VH9x3#EPq%Dj7Y^Q*=t7Cu$<7HDjr^7T#2CdvuW9ds4egtvBTr!J1AFCCVEf3 zMgC^54a(ikC&6D3dMu}bB3z_l_7|63T zXK0UTfx2ybS68!f$u%q-oQJpQp=1uCd^*(hHYTi)r)s1DP61S|#5+aB2kX-XhL^6_ zPY(%c8Hc|p3OZLO*1FV?D(U#J(@ETTz$j{CLl;aoWVzrQ)#y`mHM`O0fQ#ggyT|0$ zObv0_vaqP7b7EX7f6JuJ#%;`Fjp#Z{^U<0K4O{a7GF~P#G8wPW)Q_SxQ-|57!E?+I ztStq)%Cz!D6vSz$mOgB!JGHA!A8abDqM8APl%sT+T*Dg@kTXHfbrFja^XYY@G*(zM z`a~p~%AlrM#low1Z;jtpyp1X~ijA{9IF#&iI*8JuXOCvv%~*P6yb!K#=2!GM;7bwH zwd;*{i_b1*Sh)Rt@7!J4JG9Zp0H6CF|E;CYge`}ptq-Kx;u0Sjf2IZtpN1cn6aDFF zBT_Lfuxh!uYq-&4H9Y%G_&g}MnY2SgSuxDW&mutAa=w{>YH=G zk_?-m=D5g6;^WnC`@Y!ZLg}`f;tY;-7X`Sy>Hs^|}LwTqk9_1$JydmvIxgVcFFY=+?X3YAD_dq3Pccdhts zM-=AEF!2;ys6_$Rq1$l8q5Pj3+iVcsHNkDH*S_L4>QG9ks1IE7g)MbAsC27heq^`m1$LkvsY3IhB9h^-~r>);4v2IB&j&S$G;VT}%XPJLT->(9oZ=jXuZF|8fle&BBrr zYAPyEI4yUfp{Bo3U$l=-Fv|woreMee^mf%l`vbSrAp-<%P>O~QLm{xRuKX^1h`mD% zgS=lDJ%4GH8r-w#Pa$8W%f;v{F>q1+}=)dZ>Vn4P1Dz^sDkjuU|7QVnY zKw)Oxgo3X+wB7@4WlHP3_eYlMN>yl0x6?7rDsfyiN8S8xm`qrw3D4(q%hR1fr{Nc# zc1)8riA;R8x>akoXTQz}KJ$8K?j_|HM&Xv}hZ2aDN|%bMzNAlU4_m%6lk%71C61na zM=FgsXOK!0D~~-J@=y~mIL^^dRVtio8Iq^+oTd+K`TY5e3NuimFSfA3cxCV8ghNIq zV{gv|QBbOCx~>iU$bcg8XloM#tK>_tS_Khv(tytcEN=Z39}L5?;K#@M!ylHAb-lUy z)#0Yi1ImWT6(eft_&)^9Up~T*PsOJABN*%3@*gD)3=)gEpI0BjnYDAssK3w#U4m_` z9c%SuML(oowRCi8(~Q%^oBECCnABo^`Mk0+P>IRPV#v&4XF@9MV2+K8>b!bEeVXuh zHs!CbW;d6}Vo1(jiX7Toud=(QaVGJB@_J46D*j`X~ApvlT+S=N?#2kUJd;)mzkh7!)&XSUqergnAP7b~4XNGrg z5^#MFl~3DUov8RdmV(;wgi)t-{&Y(VD!g86BX9Trqytx&&+(9<2#zfp*`!v5df7hs zY#pznCoPZd;;Mx*q-oZ|M}}N2*eSYbg3vdKe{WWj60&$NV=kw!M{oCP6wJBBehtJO z#|`ilPN2jHQuG+4YR>FzrF?xa2ernux}zq#xUf&;gJp+*YSi^Rx0rcdIG(+YO4Bj1 z9B+7)oKEnV34>nPe~?O;>9MTfZ^8|W4qG8RLD8u&zJvlU*5BsL`c5I#%*@~Khxk(r zCcz!$dNg~>c56x+W+5c<+8KcF7(DX?6%!}ydPwY>;w~mL=q?$J>wHo1=oSXoJp$EV z3bKo=Z`y1U5}JC01TGuV*5^2BJ?$|* zk>!wlt5FY`TMosm@g%;yx?Q!>L1nL#F8QvS8 zD1H6DC_T26Fi%ngN0G==CxyIly0u7u5d*lttJ8G?;H*vq%exqPl&eg`^&Va3ZD`&` zTAqPEjnBu&2Ojrp2(~;W%Qb)+iJ`KdP;$$Agom%I){F(f1~=+U1uJ~f%6OTa^*9#M zw-^HIWS6D(6rtcB@6uk6{g86pm($?=Mx^qzdLdYnH|N`K*GST+-WUd zx-_^kHY*e1<+nu`_7`N4%P#T@tJsX3=3mMY?=BuUJ-#4%Or?EKwn&TRrr*_cdB1xP z)w2&v3!-D+a1kcNgt2>s9Y6F_X__~(XcArazy&EL4t8%5bSFUm59Big=$^E99PWW4 zuABlOY}D#p`KR43`60bQz)|pv%;~gpZCotWuf0oG7r7Dg#pc}_ukDt7>lSIzf|_&UnGLD1%HCb< z>jK=DP0D3?355b4$=xA+$7v2Mm+k7rYH1lp)auxHehDQ49sMnmTjkEpzSQ;U^_}9$ zLaLSoR}d%eYJWQ1^$6b#`K76;Y0Pf>BFh|J+$b~~#B-Xr)YpH4+pHh>1n+&2kIwh? zrPbWC=2rR`&YtM6RJ<87T?P%FBv0qOw||~&aN?oipZq_*zB``le}7whX-LY7k`Pf= zc1lQOZ`ms&o63lWkzENPWQCCIO~_smNmep5Lsmxj^*-nKd))V5_aEn+`kub|eBSTZ zcs{S^HR8LZU-7a%F{pljODK_g(Wjcel=e3lo3eUG!S7|gR<`21o#To^v$ZBAO&i^# z6?g9S%4OxgD))Ne%vw>(D`7<{p{4xpR$umG>_QgK4)$c4jvl2T8~#>ERqw-gXmqO%-}dF8<((i@+yAyUk1sMF-It%AUszc9IbD<6 zWqG;*Q3!P38FX*u-!XvkJHMdd*R_0A)-!YEQF0m@oDO3=r}@-d7e-U-M-Pf}uH3Ed zCAGTzGPA#8EU&qnRj0Pq?E1f|t&NEVqbKP?c0&6T9uKWmQ^knBW-MX6TR8pWK=1{P zvAh5fEDa`>;{IHiw&CC~w+cTN-n?(m>frBKspc~_-y-jr$6Fe5xHy~qqi|oH*dv)w z-_TbuZa8IgYn(F^YPrnvzm0}5v9a{7%hRbLGxWmupApJrWTSxgYTvY|dNBC!O2xlu z_@gMOsa@vs%UcTdk_MR?}?PWv6StykF?T4C56Vg*vjYCj(WguO>g4b{c5n z?BX0@5*U&g64w3AFXHE-Z~1Qhz{dWz-6BoSqt4TT%RFB$C8(OSZO`4d)AGr_&%1vl zEOOqH@qPOA2mB#M0{I5?LHAbkr#b~oJ3=KsGGa`~^&zyMbQFJvNFTyNlr6Kg$}&?m z=3&WyOvK!ldv0&Q{tW?68V~f|EG!y8-H$_$37s)yjUCx0GHJoZN}8|X8Dj*?0dYym zpR=>pU8}5wjk#K;ZVQa%DB(MiS4~Xa=(ex)W(>z!3Xsuf{-FP7);!Xn$-t@TUs_gX z=j4=>lJXRl;o`=B>94Dh_z+6Uh)Y(~WMpJ&6;hcpuy7um?E3qBuUYqd&(S7jSiMpi z7}NyNYKoSS^aYOF#}4Cu`%vEQVrtr&?ilfY^8NjA=bT;>zAxO#-FBW%*UeS9M*bf0 z)}|MWOMj+!(qr`Yc&u~v+(A4*&l6tUAM{U^b(TGgFm|Huz4gmxC~dLKs6QB%dcTPU z278IFN#A`WhYueX77>9_*jt$MIk~sHiDvBS>pSnCq~%IHq&Od+J#=(*0Oz#|taZjY zRo`awXY{`qmeYN(qzcah_}Ipy=gPI{YW#khpp}S)(iKONH-$NWc^B&45R#KleOsAL2|1SG_(2%zTmlZzH4d{`!H;W#zfAuYe7EmXusz$Ii3G?zwVP~gRvp}eI2L2J%|H zyx`pCWX!EEc&<>q5+WQx$M8sm_ql7Ym1Uk5A;U(l2jP*)9^n#)+cUDaMhs{YV+6%i4k$_fIrqDx^&T{JE&%_N=faN2|J&4mj!F$vF-JP|D;Da z65oEe-zfAm|C0T|{(vGO?k{!%AlhPr107b!yONY{*x7N4Zv5VXYugmw95Cp4_VMEt z)3z8Q)Q+%z!aF^KO;6Y)qW}2tVEyj*e5vemUU!Y2|xpRha_YhEZ>BjhUB6ibHMf7SI`-WF=qZf&-H zJ(MIF&_d&1)*jOw7kjxSu#6`mJ|5=f;lFwdF2E{+aI1xsJzsL!1TYlg^;udfjF^<# zJ@jrLp)`XVBVkFz$jq!h<)%$OUcD~Dyn|NI@hRcEfns?9LSn*;FvfLO0B=nO&ozR} zL^{;Y9quorwEWNTX4(twjNs!~|KQK73`M2Xm$=~UVYN~6*X=*i#Fi-)gUc3`a}{X{ zsiclmYIkN7h=T%hZ<04wz4-)425(97LwpUCA&M(+i`+29x<97SPcGyzAR<>h6`|0bfg6ZI zUIBTN+WNOn-rKi#zzNIYr}!pI*!_xQlDjU9lzzPTelYI21*;B|2F*>B#!=K(?}jHP zPk2O&W*S#iL0Yvzz0O9T_~2o5dyvThpon*(@|QebQ0TJp8+q&|=XI@g(otJd$gc~g zuj}sa{yFRVK!sQ;trE&9nL-#pL1@@qgKO#Qq$DrkKGK1AbTQjmGBuZWzF zj}JjSgk#*FiFQm93GW^hH9pC=yV%``!y7fGEiP8Q3U?w0E?nKCI1mL9*@X)iAYrtg zQTR`xBYjx4#drFSz`(#?7jEqofByp{@GIhOE4e?<07X)$yYEdYDUz*8zB@)nM(*!_ zaPi_ew_Bnwzm>&F%GiQ`owk=LUcE?I%3o-;|DClbHVz)n>p~QnY*(00u@Y1+UZI z-Ew#%xc+lyCQyYnkN@`Nh59oPdcmLR(PVV++;!RL5ZGg*W6aLxa%i5NogLdOBo`{F zXpMpwKg*o8vYr&6#yoSV^tdEYtJu1myD!OlpP zE#I$1d&Twoq;9aD)t2?-lPWQ)pr9p7jnefYV_QQY0r`biMOO9&fxaOlvrDPdsbz@Ue*2;chzyVhfrxE+K2Fp z!X+cl5k8pZUD?trCoR3*n*E`s4sO}rgSRF}M@{J7=jL~H3gAH18=GiPa9e0#CUTSD z35DZU78}fxN7s^-h2-V-E64m!=cUoIVmyVzyaMJ57 zQ_ou~$-0!FSe2@L$kSVGU{`#c3&)%Q2P=pD*R4BAZ8Fcfm~2RewC}wMOP%5`_D(`6 zuG{SXoHxAt_pNCgz_V>nH^{Ve%4)?1y@bCmaC+xbR{30}UjVX(C#F_ZzO!T};5Btt zc9YO?*4=rO;-0rWi=@m^V!^d9vs)*AR!w#ME2iLtj|$X7*jmHzyDdx(+f8xrA1WG4 zp5-W2HTAeRZ%F9rW3~D@UNSSXYF+M|6A(Cc>LE^G+xedo7?=~44{(gZH+sWwv9tigFz+2ti9Mf;9i`Jguqi(8rsD5m8yID{6StyTSn6&^80_dB)v@*u9 z_=wl?QBPLU#!aWDn02dS-mYdLLo2VUw>JtkmX?ZmTXVB`h8j2g3l@ca#jD_GTnE1> zP1tI|I%-6x<~}pwzDMYC@liRAYE{_s;iL$Oeh(bB3FB$lbRqEP5dGz5AS#DmGINpm zJE`zwwzRcXWDcuGZ@&MgraF~cIIN+!|LwNv@1r-}%L4!lr=OG#w8$`*rIDq{FSaV1 z;YwJzeww>MquWfUf7jQKJ+JOesC4)?Xojo_u2}}v&>78)=8>MKte2>fxcqzbA+wh7 z)w!=_5o4l(yfK#iZv{=ClSnHtp5onHA~SwQSsyY!AJ^t1OtEo{`J^0i`T|N>#Q~m? z6?HF3aPC3M-#(YIW-92&Ytb`WyVg}0>hQf^9COe@#e)INbWctlL(Kkr77x_=A3zLr zy#E9txrgbY&0zVJrPeyt=)*Smf8|?g_egBc3W2Kl8e?zm@lg4kEa7NHkEg8hK{h^xLDW zT3drsm6IV))5tR1g=%2D{W|?z26u8ixB@J z`_1jxV+@t>uYSnOGeMmPQ7R0&=3ZcehwV-DIyl-%DKCCxs0G}cpumFVTwtbZmrS`0 zQz!&6UeD}y7;U6LJRPi?hEQ!HP0XKY@R2^VfKDVqg*A;Ls;o=|KL&mw^Y}*uzFuJ6 zPuM**XRWV=t~`PbwGefnH%>r@***ah;tGPC45VG<|%}&a|aEQpH)^l@uy|SHgA1VK+_4C3}Xko%kQFbCyRBN$$xnxij8nn%R-y zbl97ZzIDBPj{Mg#U($#g7gjAL#_wkr3_tqcwBilr^soH@$BxDIuixM6RanoK(4RQr z({7f&>{?MZ)0%+>l4N?Ij3!U31y(xEFv)H=HK7{)p z^y~9~{*1tyjtNsxfMPFzQvi!->A$AyJW4@C#jFpU2x<6)vi}?l%MaMB6NcQ_WEWxD z@S}Bn7>`cf<#IF3X&{Zqg?04k(dOPS7D(%0CVa=Wb#yGJUzft}0o|tw=Y~1XA6T-y zDSA56Vh68S+rNLz7hPak;8HdqJcrV4iDBCVxEc_(E*wuRatC|}PFm+~-(Qz6izq8A z)7=PAa=PcoEqC*hXI9X9>>f4KnM!3ndIlZ_}QVR)XuwuZWUG5QU< zAZ~}Tn?zrW>XqMZ^@DQ}8a$XXHbQdSvPk%TT)9Gm*p>)qT^#a+70rKePt-N|I$u&0 z&k&Je5WBN&{>0H0`x&Q{^ju*dM=jr=+ZW;1jX>N6sGNv_Rvn#d06rsaMDxPWM8Pa8 z*gqk&M;ZEd)Z)&=fqZ8D#k>kzqf78mABF!ZS`{L>t~Exa32qLA!x{E^HYN2R-Jrpl zWmlAYn8P-1L=yg=9{=1I=cx2&`-+6OR)-10D2VO{9<1old2Gzz3R2ZkOfx}(3%8$t z(w+Xur)8TN_cUpWw`*?O)LJsC%-cm;b~(4h1Xa#8Ez&p}MZK}NEg8dm=?@hxXve=) zd|e!__!y9$oD}(?U%wg%<8*W=)-m0Hg217=NF7Lc zo8mRWtpVoPmCIl(9r?BD>yb;eqf!h zaYWoa^`*Mr65MctJhr&K?#|GqyDvXhIH{>=4_{?wp<77EY~V3p_>mWxa3r;3vvtTw@YKq<6#QoUXiY?>ZdoJoIC1JxYiA8(Sco3(8h{p!aANl~$CEDKOC7I3iO7;;V%K;}R}D#S<@3b9ur7N1Unt(FZnvWLd6WL)Jwlw@ zet%kG=AX4KZM3F2^FH{E@B4jCrQux$#)p&LoZa)t$w^#%=@O#J`e{#c?{5B)H>i6} zne|2ESCr^%8MoU%YnQu4NcbNoJevI1;MNIz4sIx&Z;p}!>~VuFcqv>|H{b+o1muB% zllYK4ln2Mj3%q-JY1Xq#gIk$0v$=y7n~QY=rFsTwi{6PX%}(h=+P;jao`Fejrf&0s~fcY zx?q^2@y!9s0eRQheLhUK7QfC(3JW|&S<@*|SS8zO!3**pMjXg;QNCco)|Un*gR+9c z$DcL+zJY-f8OWqD!##9+X|ikVFV(@TAWNYTVT5O|F`^29un!8``xECC&(6a@&Z7SB z;DbrFaR`7FII>$OPhU;Z=aI&|IznD_smkmnQuNGWIb7{AHxPI)s#K=fN^mOVt9nu2 z^~>~GZO-)n%Kqr}EiD-t$L?tX!ll`9e;>irNOV)#!Tf~0^1Hs)%luP?Jg=@;ox;GT zklNac`V|tO|7{NbBYbuUfEEKWvw>1)ow?d;sVONFVS-e!jQuciwEZ)!fW0R=cd`>F zuEXMFU|;}u?n3~*4cdEiF8llY4-fv^BFrmEskl55V0Qq`XXqP-i^nxw}Q5MPAx%-iuj1^E)DZoDl4`+9dEL9HIjv>YidrF&sruo|lRrx50!vB`2Dq-!~fIAk`W^N+54e)4uX4&tL?G||( zsORnsltzgD+Xz8(y@=m_Nw|KP@u+WJH92vT*yGd5;M2iQ{HaZa)9_y>DiPOrR3d`0 z17-ft8mO)RX$@5#r$|D)b^o`5HmWDvyh+df7!A*%Uws_jwImmEuRgcISZASFh+{o# zrB1w1;=?&}@)jA1@JHT1h~gSZ4_@xh>({U2TCf93Ac>|R{bb~+zHl6EXy_~9`T~oP zI9QGY08z|6cGPqCyAL0pp;ZKBwk6LRXE*jM;rpSboRI(s73ixMBQY9pO`hxlXf!{?}N+EG&D3XrAMt|)me@m=i3HH z`b(EC)sNaw&&+JIwY9y%6r$Pe;OHpM@Lb=(q3Y5SK<_Y zjEs0jN1p_!(4p zh2SO+2@k)Hdw1{Q!^~i=U?bde^CjPD2|K@GtWgYp2&D2wSeQ44AA}P*S`=m;p3d*5 z_gC#`(!Yp&1j`uAmjU@yjE=)#1$nrvvKLcS-reE|uqn5FZ^o|B&OodWd|{>3PndJQ zzJH%mmGL5$AVMr8(z+sVy~2r(-@>k8_e2Y)+yTrEJGNMKQ^{!k>bl`r@5#DUyFLg{14YhOl*5lP!Jli=>PF_AtlYd`>N{N zCu6SXuU^%|{aCjgQtOJshnOj@qd1q#xj);pq zdud!(`9u!nJ_goCx+hn~6co+{U)H+W{obP3@my`?)quaJRbneofy&+BOU?>H3K>Px zC;ewfXtn)7fqnkPieBJd8VLq)U$nkN5;R4g)45-Bk6+VQi;K6ci^ipZeu{IG%ApEsZt#2fki^pGoUlGxWPay`L}&2G zuO%t9rz$fS-Ckx zM&`+g4yAJ*Tjp)^!-o+6!@wc8@73PDpUIBK$0;WtMI->zMFoGU5Bn*yb4JClv-$ce z+ApVLq+l*@o_g%wJ&|ZZip7b9nm?1O#+()bT5reV`y~uta*myA-~dV^{D;odOsrY? z!=+bD#Y}2_)qk(Cb#;`e+L)R)r>k`b?N2@_$KB<@D&p_5Fy9sYJi<7SGD#sEofi!1 zkRIlpbGywRtVLp@q^j`P47`Rk0)8|TeMLEiw#VM#xb-K>W5=xxXM9}+)=;A@UWYmJVE$vMOg(EFdd}s&v;@n|m7~c4p zVkQ`aBM{?2shc+$0a|?eB#x#LlM{M+dWVH~#N+hz6uzj(zbo!7f>-R&HWGeE{{Cm+ zTM167Ll)tvOMc{tz8x9jy%_(lA2=AKo5{?}+r{uJ4A0u$K6`DhADj$q08HYowkl3D z^@7HFzC&L-a#N3oN_S>|U@FrU z!>U91Yrpi4ecwL(3*YVVD|7SF8_Y$0o)sR>1u;ML*&jVh0xd1eqP^45oO9l9)#s9d z!KeB+VNWRCAMk^IFt{|OJgxLi2hVuN|W%!3l3+!HOpaY$r0=virRO zSHy>`tjeaQrg-z)HN!CS*;8(Q`&wf6R6~9J#liVv`%%9&JA60i`@+o32z7egtc<}i zO6Fsmox6rEVMdC-jj$@Cp^?D^7-y##^nxm^=3Vb7K-zfgH1!ppRCsQu2IQ-D%+-d8 zVVHsVUovv?Yg$^=B$zFXtw`Xn1R_+|lHophjJ&~jj|QFro)*T@c5B%%V!WG}`0YiQ z>61zY_qd6nxMVw{lLG@L_g%v=ja*ZicSw*l?0mK!r`A+o|KT)yi}xQ#8ZNFo&7$hU zm#CXOPQ=8RtgcV}Ug>VfbN28!`RI{W{YmU!jv$Gt<{+*|AKOkwMNZP|Bbe)@vtC?b52LTTIAm~@yiS!{(9g7-Xr!XLWPH1+rRjT5 z#P2`n<&_GHC(S!k-0U5=Kc_sWySK8i_Wr%g>*&CPKN^I&1Y zL!JsVG>pct(uW4!g4>%0Vc7Hi^NRsyC!GBTKZrWQpPo9u@ zRXxqf*yrlH!(M=2kouW))4%YBn`6cMj^0=+jZ!V$l9WvN#6YGHmniP1tG9JpO^fwv za!BNOYL`-g2jJ6^ri$R9G`iWAF$)$&mL(!`rZ6{mx5!NS(Ck$=R zVENLXJZWcZ`?0;9EL7TrQ$R@QOh^OKsBwfFp+O4_Jcz#R)~#E|_Is^)kff?;{gQC0 zv9x}R(ouEMCQ&V|g`JRGVlFZEk4+brk+_Z!IZcU<5h^oilJm4=@uq8PYJmAic$C{| z6zs>mf6Oy5EsYIDIgdr>Q-mu;_#95BEYS^kc6#p$k@p3V#uigzNCF;7CYPoIUGpByepE=Mh@T@!6rB<=$Nvoc7s9SbRTzuqM@A1$Y`iu zVOm%AKb=o9Ex}F63;ydr4bN$1AoEJhE~~Sz>{I0H*IUwlEXSDe##2;A_far2C)5}* zCoha^>l_^W%6RG0VRy^t-`|E$6y0&{0rfj0K$5wANZ;{|gt@;<#D~?IUnfj2MK`p{ z2${dNx~n(U7;1Q;@-?k27W?<+T}Ou!LIHjV9)_ZZhFSTW3g>tC z2h4cl)zYw(;lQck%GXRwON#>uLtP7s7_Db-zzo)PUBI6 zHUlUbhcW^op);ZZK23yvF;Jb}t40LTV(AOGtzIMsW@pdt!ZkqXC@b7I!7j9kc9ZlQ z@zi&ckQik@(P=>iJI5C<7W`Xj=m%5cYpPrDAPPv6EG%!u*cQ%bDVam}<|(~o>v+vP zg;hr56N}Mj>D(nI;_@PAk9l)X(Pk{Mt!Qp3;v(75@k;y5(L!qNwNo=Yic?Fvt|UHI zaAnmZkhWZ{j3S90FFkrv)4ghEw50mpOL!@z@)W3~m_-=ezU>no9bMx92vZc40u=T> z*sw=wX#9hN4+CojvXv~$zvW{jF8@fox~o*yz|8k(G3Jw(ka} z{isDu@CgEF1&+$gw^8?pNf9nSZ>Sh|zpx5nCqM2-ncGou^!U}Jt2@n~KvnZu?aOf* z<(7UE)uv8T3(lumMMe=ZPAk=ZYNIET^;T2nufM#}cwc1ENpd7Oce|Q+^9KDIm z9MH>O#Kg2BVu6@@fX7l{cKY<*!w*sN5LgdbV+fP~VdJq*0}9U@ptHQ$?$6;#HS|bX zwW_Mt_?ohgvj6epRy-)^fBDV*xJw3TlRp-&T5pU>zmAXHN<@E=wCsK1x&vJ4)P92c z9pS;VwWlK0-RMdsiRta;w|1JtT0LO|;HuIM`qC=6F}TD{R(}s60OsJml}Opn1CJFv zqG8SooYoViA~E69g`2+g)OA%K;&uG~%q1Pb*uw-R!_Q-OW{Hu~$yrb0CP_J>$nXeQwM$&3;j^|%9a0npqc z2x>`0N9vL>Rv--Fv%z@5)j^C2i5xU=OvN+1iO?=0r^deU*SU$Or)r_zezKKjqg&&po3!#ob``)>85* z)6USVH3fm?Ci8hUq)nNd7c9C>y4R|P59D9^?x!AcBGaV2_Q&H&sUxbNv1>iQV{z7k zqAaGQbQH;$aiAc6J#-_b3dvh?xQMO?(H4ys5uqcP9#i6-VIT#*Sgx4o*Lft1W;u+@ zq0KeT8YCi(L6nhT3i(eq_T3qa`C={0^U_O})?tm1C#bq3>oCZHd23IJ* z8?rb=HJcbE1QJ3GwRaiwh1#SXrP1@3N-oU41@ma`l5_a;mh0JHOqeBovuX>NiPuOl z9w4ZPReyFRGn0~%5;2y?If<@{8yWTlbAN4deA_C?(Mk~EE*KhI!R`4Cu~sC2jSZ2fgGpK_UQ&Pd3<@qzgQ}f`*)a0aF!{jUwT&1E zgKB6Q1CjGKjRNav=K)NhwM_!LIFS@U2=~w>AQ4k1&oGV1Q6wZJM7}tJk-tGbLV~t| zuTEpAb`uAA>hs}yC7b1LYg`z>BmD=4722N0*k$J%+<9Y# zte{+yKdJnfu9+is<3{QN+Z>yu!k*|PgwH67X|}6#^t?u&b8O(wlT51q*(A8KM`=4R=)eIj&t@}2%20a zbMwGYLpozEXd>KVS4D7wH(PQAD_%`lZCGOtc*^L7Gf9CPAX*P7lUQ0~JK7KGV*o=$ zb|5ccd2W!1>i~!Xq-qhLg&64*k#k7J*+)uB3^8!ME`Uvu1$Hg?T5F(2;3nCDdpa59 zOT=@&1e@mP+*}(-B9xGY;5Z;$*vDq*5R!>&o*UsQVC{Yavhxk81b|?x^e^IG*o~J) zLgY-ret{%_UX)0i$p%Fh?^^+{iq~z`mV}_(Wqc!9p6zcU=%%=K*AVPV6jCUa3HB3_ zpapiJzZDT`1f~;;xWQrdF84M_OmJ2K^Q|KWBSyg@wHPtXFD@EYKHf!1x*OcBHV`$o z5l-+R?A8rzW&YKHEOxZ!gkB;5a(wQ=7q5!iA*;BGVHCbBGXuk8q!=KiN_%QubMQuz z5*Vvb#TkZhq^6e4eC-Lt?L+)t4^+{c7J13Vy* zUY``bF~CII{L9;ekU=qn`wel;2RyaNOICg;TG4q_n(+{cZi`@{Tr+HqQ@hIRjV%th4WHnNzr7=9W#g?_Gc?5U=5xICPW0(sZ z34-TrE5@ef^KCKZKbTDhsjpq z4RB)HMuqDbVkL4}tt1vZ#_k^vEyyOhXz=shOjm`UHLKxj^CI_t6E7)QrR`3SayxS4 z^WO4i$TTsBUR9l$*<<-+ZMCuPW@}_Kbue%9QgoAaW@tbYRUH~kL&w7?!-itCOYR=T zCz+mRr7wDS+TPA?+Z)`dW6mco=pySlvqLNv!Nnwi_qXZ)mwh^0qVl(Uv)6BY$wh>O z0GbjLiLdX!1VVWvk%CBYqd-nR0?-Kna1XLfFcsK-3K!o0GEiCQ&Hn!kQ~}}jGP;VA zwf?@s7C*8ZtY00NkNSF|(!z6X0y!SXU8;bAV*)ntHh9IDaB#T{-?elQrFjf3(eK!Q z8+$Nb&Z@|hV1zT@5!QLrv8Ye1M$TowrtaM6dz@3*v+;W8K6gP;P*6!(DUD-1x|Ufv z{U{AXvmjt!Z5(E^A1#u4_9JcaY$DGm2J?2Q!IVbI?Rk$jh9^bui%dD?7h9XaM?lRr z!}R`^t!}e3SsPcIR<8%~L@S4X{uI;IjmfJ{Q9Rm^A(6+@a zc^L+s!=FEYK-+#69c>lEhjX0>EeCDLY_J@B?r8(Wg?0sH39?RV!XzMFD@!$3xNxxI ziK~b^h>%4{^caV@p^PI;p-dS=cc*$+|x!J)4D*%Q~^^8O7f9&W&1R@rq$%SvXuDeylWVay1D$At#< z@!_#Q`(DOdwKu1}uC6^p;~c-R@6se6SJs^PSZ#|*vh{e<>MG74vBNx&=z-dXsltMj zGRL{L>4PzXq$kgDaUI1NvFeL^vI;9038AyWG<-p*@CiapgRvM03SAaLxq-OEcM-a+ z8H-yA3R5*+*SI@*ci~QR&P~~f#)+*2`2**L=h7U06(*0- zl?(hm*cK)tllt!E^}oVZ0f|&@WE;EGH0#<}81Vf6(Ej@M3;ojzK*WZ5%K=8-$zczZ zzn!@P@DsW5ASC#EY%Rv3wuGS>(f;BL-sAqu-D7f5IMd1E!S=Scn7S2|HNfg2Tbtv( zCT?PKmMO#+x!uxJ##~fHG$(>BN1!(O{cfo}=2A=(AwvM02B|svprFlI^gi6S1O5y| z?}7|<1;oZ+L_tU&k@gQ@nQ(jn&#mnn_YWoa*7vg*7Y|{lDd~i_o~k11_Z+Egrumhh6*;m6 zfyqWOv(L_5c9J`Jt>s$TcK?$;cYm8#CJXCq@tBA4EaWnHv8Y4;CsXuKk%dQ@2W(BB09E-2;8GmPENcK+)XZT z^Qtj4z^O-~TYU~37?i>MNHNA>+hHcJn+qf?BH;Ob`CkiolKm-#l4Hoe$W|~Kik6dR z(a0xs{E49HqMSURpWxBKy$m=7CLq_9mB}HysWanIQdO;~@;X4snb3Rwwit%86xY)b zZVuft*48e`hkkzhkrQLfPm5d&!-C%bCZP~~I9=BnUc||0=HH>x%E;HTS_JVL1gFA& zYrG;!Q&T2&UFWkz@M>ayT}{ol56}-HTL5Q|0Z85?eRg#x3Dxh3(#_VTBCAU@xCc8d zk(xkz$cN{E_w8ui%spC`58bxb#<7GDkdd064(;WcWUu1@w@WvddzF4~2kGn3N5p>y zHW>eeo(-BP8rXpW%0hioVH}&2w!XDK=Zq(He>$s9N?v|0cO|r_mz*#9Dxy*X0|QU? z(BZ9enA?2Rr3?0Woh57JJoCwtrO!`hE9Owdli_#66~_iT$Lus5DE<2$S7p2j&Nz&h z+i~9f(>KHVr8X0t--3-Bjg%I2jTw0b_Sc3FnrTPM45$%!#5!1v7lVZt_M#Otoc~z^ zzi-;=PLxP$Zb>=HRo0vcAxaDpZTSUKjQH6?*O2D2`UU+^d*70+?m2XDD zib_gV_>$}yQT%ocxI9r}s%Q}>JR~*SKVo9yOoXJevNB-2h50jxl+BlXb`d--Lc<0b zN9=M5ciwzza*QF$?PL+%3^n9e5E?+F0vllr%5rRA@W%Hq`9$gDwq{RFLqjEe|04dH zI20dOEZe-$nh?5LSjiA-BD^VrK7*2w5SV42H zxwo76<0x={f{~es_+k7#LZwsaY1XZ(eH0;)WWPX27pRz~%7TC<*2Og{+ zWB__{)r?vG%5#I(AAv9U_S)`pbfMAaI$`2&GLp8K^pW9u)V1jBLq)gbaz&`0e0iv8 zJytl$PhH@BV=3g7lBieZSY^H0zipDlhAi^)_m2>9y`-wD3g{sGmUYo+DF{$;CCGp% zc||@!mzJCR5~m;mFJS{zm#qxA4y^AaBYI`Wc!bu-*NWZ<5b-`+BeU1e6N`X^GE_2? z?l{7p-o4Dw(GImfdnjT@nQ|ND7s}c6q7YIC@8DbK@h`9XT^qYHq^56`wNOy88HmD; z3w(R%09!z@deAR?^@yONk^E0+kN?pDaE(vO2DinX(5z;d9e~KUvEe$^qfmJTlZbzB0Dwz`$jQ z%l4*2z&iQt?G$Gg^;hpt@$kKDi(P-MBXpPZ?z9G-+wZHWrH7_W_dmAX2UEp)uyJyr zT}9gUVbP6?h${?N*9lrYOB|g$=~I-ya>DCM;5%*11=pB{rH}{xiFeIac(P$PKDg7P zTCqiOnyciGkVm*^DtoHZNq>A2-+b{AYegYVY$xVa&umtlSV8Q_@<>>3+C|FXU$uninl{(@QghDyz?u-`n zWIniR5f93YS7qa~;lc6W&uG{9`h1XfvyTD2y=8yKM%G5~YRSff{`9*J4cgORW{nUN z@pVsTvaVuqYwESBuS%IIbsRavf`WvaH{vbkw*W;*&BsE^XdVj!!P}@NOUk?C;Cqm1BwWBl9t|2M8FsH z*&m>mt)q}TF$5g$aq#Awe~(TW=`wz%s*mrGAkEhP#xJ60KV%sD?{fKpzd>_LgUY|u zZxo8WHn!w?)OzLJ#=BO2WvR%sfm`gG?Zvs}Ta-loBBawlXYnqy>-K{H5C_9a!~1sK z{oOlv{a^OhO$B*<$xJG<5yML?S0~VC z;{~~XUg{I9T^IfxGvV4xdPjEAN$>=<>{NWWP`gFH84t(y?XP$4D!NEXyK|4w-a~f{ zFY@o(Z?AvxzTUngo-!HInl(9DZQ0!V%qyycb;GK`_j(6*S@+R?ru*UZhJID|pUByF zN2^W_1#kt%$0pJ5PJVP$)wlcRX_BEJ+su!KA51Rp_DS*X*F=tptgI{#FE6IEh*ZNb z+9CMv2mcM1T$aZ9j*Kn~xWh?LHJfrD^4almYFs%lH&;KWNA6apQtJ28?U=s;C0+U3 zv#61$h54amTG!9thDxm=Tz7C3g1 zofHy=Um{|uY?klRr4EoU#->2jzrnmH7(JV-a0S+_N~xt)+kkWbXf}8PuL-T0Um_;0 z$4u`(K1>qT;{Bf(ruL2w2Cp+QAFCI?DPKF@=HMZl{3Wh?3Mx*5b@8O~VWs}FSszbN zsg-Njt|hgd-7E53)iep|7I}KSl1Yli8A@u3qv~h-zp_}`4#kI;ouF{1C=^&veb|xl z>6`;szWMoJDtewUy%C;C$TrO@3dR{{1fN5$evlBU*t@Fbce*UV*PNWIAYfId?F=eG3BIcs4 z@oG4i{O^YkUv>9YRvr=)({ep)md8Qw9{Hu`4p%Dkb)D!Jk&$0}_4cr|A`<%*&pS6Jlsmu+LBRcoLL_U3e zfWFR6%<1vqpUs22eI7YeEEcA2z2y*DeA;4~P+Cfh`8}L@K?+IU)K*rmPW8b6N*3dI z@aBL{fqp~yzyjStV~QPxa3!U^LzC>mDfLQ^jg!u%)4+j;pLy$~=ua4}K_(!ZJ6kYCJ83m0N?>4k- z0;SLJHd3>0vS!t@cQ}k#3&gc5RM8rE_EsnLDqLq#3}O+ZJ)M3?vxV>K;E!j~bSz?) zEZZ(-88C3CnI5$Xv{|$>&51ZEboT?QCQO_fP?jmO93vtzA>sknN6dHm`3r~nf=+2m zBKC!Uq2P4M(`eu>Q+a$=-x9uwmoG_z#{`_uc9EHg9rn3pfTKM_yFJEpP{hB-Q&dl6g z1b&YnzGUhhNKIw`chB-PN2_3nL!Lra?Ltc(ZD^LOrVYgT8755Tys9$zn@Xy2{A`@rp#Nq(a4i?>fVIHS-092x zl*!Y$W5$!R^fs%W`CAC9!PG8FuF|F&LAv>C-f*5p@8glhBYJ#OFKioMkC-`pi|eh7 zc_@O5^o_)T*wZtfqz=2UMftoiXg!s2R`zelo$#J>Y80-!h_W@E%QA}F>r?N~qT<1> ziRiyyKPJzr9kbr$;lDvc@A=@AY~VXbHoJP-n`W;+BB)&IrO4!q2eWVAGJ!8l451Jq zidambIai#VEaL= zu9OBr9}XQT66JQQZEC+T{8At(#?)Z>;%sV}Pp_rT_3`Of9^GgMG0F%5B^O?97-^#- zI33<}JG%W9*!0$#Fb9jP5xsB3J2%6R}hH3c=@u^ z#FE1orpHD(4fT!t_3I9u?CIGW8MDahG4}KlpcMXgQ_?$PYxC9)s25-J-`km{+L~o# zap+`6wnv5N`m*^s4z)Ak*OZhFP*Ism2HhEJWRo?FI&k}CX6tRmKMcz|r-$DDjk*tn z%B9D3tynYs<+ZU>@=@3GJubO!u4G@(r+4xb%;jKza{fuZS*t#(_b1IyN?op^Z1z); z&^7S7{q{)Ftst4N--oYw2iUsuFS3qhef^P#fT1deB6lb6%f}hP&>ln(qQS;?R@$qoq4Bim z&hAqPG2PEbWT6AHndmPm2)@PPn0U9_L7`J>oyF|n$yN&fiD(xFTC(V^h)a8{#~N0d zw@KwPEl72pxq0Um%fa!&m)L7~FCF&3e_XY&uz2|O$i;K#eBs?<9t%pyO%082XKlhy zX-lKRb;n@8?KpduHQ(yMNOG*> z^uWo7H4LznZu}hALQ2&^E%m7;T9;R+(9yy&bb4@3UQNw1IP}S$yu$vAX3xB`jAR4o z`o&#}B}jLW$>p3j+hHS7E)l9naj&zl@{DTmk?B&m+MyVQ=u=Ii{&)V}$=;EjBzO#U zC|4x6maAGWCvU3E*iW>bP1RwGHq{mK2TmK;w`z6D4yhd(daAEmBmCZ`uw2KAmpTxp z7sS^#H@|a7JXHmBY5VV8ixU$Q2YOk=3P$dhoMU5Koa7COjcr3|2>b^eO~NOL7!|s> z2%^?FCA_gwTz4QmM%6k=TC>aahwbF={qoQC4wosyM|W9Qx6)EQB)MgH zDW%)_!{4j5Jc0Ah(J^?OP}JPS+=n2;VB@N&FnT@2)l94c2!AjlNu)Ew&J~Hz5Bkgv z1nAwncZo6g35Cjks54T~qo9b!J_p4X)Y*=TU*!+h@ijVTE|@;GjrVD6>&7f6#-`%n z&s95K!RQdSOu|2hsWyPKJx#qgv}xC%nT65^qw6HS`Ma{MqJkQwMa9HuweRViA!ojH z@y;u%B5l%SGA#k)Ltm-CW-}V@GcAgs;^kh|>6$D46aGyGU%Ccm%ZP33KdV|CN}Zn9Xm`nRZQ0rkbzShxsGX4&QE!JzaFRXKDon96@p+ zV(Zc7!-M}>6Jdu`H?@uaQ0G23wzQ0{&JW`;fb&Lm*^M72EBc?F zlKlq(DiZmcIVG?mXqK{&7T=M1G^CGFq)xeQNV}RXep)fQqvVH@!|<~6sdhT_gm`*4 z3?%T}L7q>}&sTG~2~Ev0l?@jZs8iWGKfGoe46_JM&AEsCc0WCHUkf`cvDT)mr2&9G zi)#pkj&n~z#+}X@JR{EE{shG$Ot|#RH!nSXyqy;%!uyKyN0ymZhV@5sQYhL3N5(C` z<(_*+yoP$Rg9ZkNm~NW=;Hxk{b*6fHYHXj5r-b#;@aN#Q>)%9wJFMGzv~2b%KRC>K zZ$+-#KSRyI8 zH}^I>`zQqK09dIIvkpQ767GFr!7ITXag)ffj*e~tX4!+paJ95hRBbn|)}$yVpceS! zq+C+UP4RsCnaiQpizC+Uqgf@0-B8MSr5DS1oPxq2YlF+x_rH_~qH3tFCQ1R*Iijj( zVtRYaDf$G*$+RxkB{zs$Rn*lXp^`@J(EW30tzcABZl)c?=fDlFDM|J!U+dP<$2ujB z=^A8>VeF64kx~ie`<_#tEc&@Wg!1_39j}E`4Zpm-Qohddbo!3R-#=MgP8K3xlDqw= zDYlb1H3|;w^YH09o#vZ=eNiv;U(M3N?tA`hTG5q(QA(=k5P75f)OlOZM%DJqR9we6 zc7d}#@=v`WUPH3c|6D+5pb4B-pcDD?EsaX78KL5o3Fc*s{j8`^&aqCul@hHP79^%R5Z*YWMrl?i%?`|B(llQ2n{N` z2-(>yWM$K^Wv{I4GO{8S#sB#}=l=d4kNYSsPIej)3)SP@4a$xLj>@qrm)<1rv9Az2nZad@eQa4p^-g;R! z5X@&3^@1-cJkKav=2*wWsVVc{t6h{;zu8HB{;{dZiyPT|^XQk$TxtBNRhKExsXw=W zZROX!ek9_~C}3e?>pnjp;c52eRtUQ*5W4a(N5mb0c`MwVLxGIMfi2_I{4z1yZJ#woZn}nsdr!b}w}VKR zU?gsTyl&IFiJGCdW5_rsKH``xqFm5Aba!`O;>gDDs1EPhA1Eh{Hsn91eq!Gocz>O3 z;0WV@jeXNPGIG?*%j>Ae_(JoHBYIFp%+*mxbdHy9&HadTLIZ~!UAx(t9I1q-4>;P? zhT)xz^~R2@d_YPi6?>ja*-;=cko?@A)6eO+-e1iaQjFDUo-gjBqG2J|(N~U&qP@E; zdm0NXWuD>J7<$-cRfVO}-QR2XKa-Z+vd<_PV>f|mW6x6 zXJ0IhiXP>Ve)N%hV~RYBtngw{K;yv=i~o3D1gEfU$~Vr|6}S52)KngPPYLZdu1F$p z{93Bw7;O3$sGT^^N^>d4@~jlP?Q2p{BuC((r=CncN1p?w8$Dj0oper4PO7#Yz4G?b z(hnU+#%v3JyNSi_3zT~Z4k!{k@Cyt@a{DgNIf*f0c7f&`)W?*o(rI0&t5h=7I@OJb z_Hg|l7r?zze}7@Gh)B`Tk&HG=s3S%X20!NJ=Kf@5?ZxXP!kD&GD9%BuZ3WMY{o35Q zw>R@{6c466-KLW^W@B!ikdoq4S10#MgA0Fz80zF{z1GPFivQ7MMZ0(+cl)s`S0}Zl zq@~FyC~9w?l zIt-OhaRRE8s-sm*@Ve|3q7(AOGGL

*HVJ*}TY3o*1yqz18&mB+v?-yLMD=d(*la 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%0Dd*o+$<_!3t%r!q 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�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 ~Aj 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 (Nb*q9$ONWB3M%XCMwhUvc37!ytiq% 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 PF`b%fda6&A&j$Vv`oYZ$ literal 0 HcmV?d00001 diff --git a/doc/surya/surya_graph/surya_graph_DocumentEngineInvariant.sol.png b/doc/surya/surya_graph/surya_graph_DocumentEngineInvariant.sol.png index e166f8f300a14e64f1ed3caf7e86aeb6aab548c6..5b4fa6b18c7cb308124d7e6563e4c278e53087ae 100644 GIT binary patch literal 13934 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�M1HUuhusY?Be`8>p)Kp&Fuf5Vv!h6EFd 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

D_YpAigkQ4eZ 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 I{Nq3S7v;NrlK=n! literal 0 HcmV?d00001 diff --git a/doc/surya/surya_graph/surya_graph_IERC1643MultiDocument.sol.png b/doc/surya/surya_graph/surya_graph_IERC1643MultiDocument.sol.png new file mode 100644 index 0000000000000000000000000000000000000000..4f3fd1b2bc0eb09887dfc52e1ff79f6f0aecd8fc GIT binary patch literal 42431 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�n$H=6wtddpDeNwDt)QS=0j5RW1Voo8@8 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 literal 0 HcmV?d00001 diff --git a/doc/surya/surya_graph/surya_graph_IERC8303.sol.png b/doc/surya/surya_graph/surya_graph_IERC8303.sol.png new file mode 100644 index 0000000000000000000000000000000000000000..8d0dbd38e7de5a586f3c07bd19dced2b8a8cd731 GIT binary patch literal 15807 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∓%Uqlm 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 W-}gF&4>o*9!YeE|LeLr%C;tnJDpE}V literal 0 HcmV?d00001 diff --git a/doc/surya/surya_graph/surya_graph_VersionModule.sol.png b/doc/surya/surya_graph/surya_graph_VersionModule.sol.png new file mode 100644 index 0000000000000000000000000000000000000000..a8db0d5ace8db7b5110c9d55656c418bcb6fd0e9 GIT binary patch literal 30796 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 O$x2<5%o5jo`2PS0*-^#- literal 0 HcmV?d00001 diff --git a/doc/surya/surya_inheritance/surya_inheritance_DocumentEngine.sol.png b/doc/surya/surya_inheritance/surya_inheritance_DocumentEngine.sol.png index add3eb2fa2ed6c8f6d3333430e49f3eb78c6ae58..d0de70bf2c6ab56c10cd4c9256221047b2e2358e 100644 GIT binary patch literal 30983 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 diff --git a/doc/surya/surya_inheritance/surya_inheritance_DocumentEngineBase.sol.png b/doc/surya/surya_inheritance/surya_inheritance_DocumentEngineBase.sol.png new file mode 100644 index 0000000000000000000000000000000000000000..91f3846f9a0e84b72d6d6a59592f43968090a834 GIT binary patch literal 29985 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 literal 0 HcmV?d00001 diff --git a/doc/surya/surya_inheritance/surya_inheritance_DocumentEngineOwnable.sol.png b/doc/surya/surya_inheritance/surya_inheritance_DocumentEngineOwnable.sol.png new file mode 100644 index 0000000000000000000000000000000000000000..4e2b439013ad5dc8cc704fa5d56c3f006dd15aa9 GIT binary patch literal 31633 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 Iau%=u2Sj`5TL1t6 literal 0 HcmV?d00001 diff --git a/doc/surya/surya_inheritance/surya_inheritance_IERC8303.sol.png b/doc/surya/surya_inheritance/surya_inheritance_IERC8303.sol.png new file mode 100644 index 0000000000000000000000000000000000000000..8d83e9123c3f152061548c01a7c4330b63139fbe GIT binary patch literal 4282 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 Ad;kCd literal 0 HcmV?d00001 diff --git a/doc/surya/surya_inheritance/surya_inheritance_ITokenBinding.sol.png b/doc/surya/surya_inheritance/surya_inheritance_ITokenBinding.sol.png new file mode 100644 index 0000000000000000000000000000000000000000..f9d8287b931c51be7c7eb1e0fe48e33f9a8e726b GIT binary patch literal 5094 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 literal 0 HcmV?d00001 diff --git a/doc/surya/surya_inheritance/surya_inheritance_VersionModule.sol.png b/doc/surya/surya_inheritance/surya_inheritance_VersionModule.sol.png new file mode 100644 index 0000000000000000000000000000000000000000..b113a03f454161abc7212f39f292cb6e83462cd2 GIT binary patch literal 14644 zcmXYY2RxPS|NjZ0j8Kogq9H|Ogb-ybgk(!(lbwu^U0I2YB4j0HCq?$i$|{kF>{3QU z8UOeF{rx@9t9l*h-1l`~igXP_TZYS{@~V@1-V#fad%K@h*RnlveKxm$U7#f zHpWKr&K6y|Vk`UwiMckZ#+7ykxre5N*Ur7!u6Is}{G1WRM}=R?mP1s@Qyc#pd45k) z2naTEjmz`I{v$2Qd2~Ap&(tnT&!qp8m*wMg#g8ndj1$fH`1owKD`i7{{nF6?|LeyG z=DK=%_ZOTM6&3kKMaouI{Er_$CXpm0ByIv~qHCtQ;JTZ{ITb`ud*fP_(e%;fq&0dkZ%Y4h|m1^Bfo$ z94z>zC$J~KpkN!RA}RXJ0X!c$M*4PWC~|n%SpWR_%&aW3si`TU+fOSi4TLPBl*l+Z zI1Eip8e3W@B_$<`D=NYg5_aFee}DYz*Ul7LP0TLe&M>m)E~BS>ePy1Sy=+NV#=w_X zR#Ki45B+y_uF+JsizT6`NXX+}WqJ8mNtSq1;noKyuIlLPhfYjfYLTL$p-Fj-Kip=z zxu(V5y?f^nzC56(T|YX?<>KNpI{nIy|9-G16LAj>jh)Ai9g9m(kHP#|U%s4KP_RQr zMh5F1R8}T-|G|SnVTywX4<@Ijl?hoKE+{Clr2Tz;rK`KUr7P`tipRp2FBH3W?W%hD zvazv|JUu=A$LcCYM$gajj6K~7;tJ=^G2Tf?Ncol#xM{hVL<}VH0|p^4FE4WJ*d-&c z-={Klc#j_4b>W%0vWg1jiWomX`LPsQvS8B2-=85F89eLj>kLdxWIQ}Pv-9(%LN%K8 zY;C(wDOLIY6x4bk6IN9v)zsRWjddi(?Bx~q`t`}OYkq#uSvALJP512FURqk}x-_9| zqp7a0h?Th=9nHwf8u|9EPH0%zUJ(&uWt}wNIY^OTqs3bpI5=+g=V?!NrifMf{TANG zv?H9J>n7e!K|xXX{yj@eON*1{lpPhe)ZE;hnuf+He}7r64EgBwb$@JTekm!|%*;&c zUAt6ubQs>BT4T8%Tuz>&8?`di!++oaWyCHX6+Gqb+qXsR-tvYlU7asAIyvXtAU!j+4@?6_ty);peGwb=2! zFsGdFp2WmN*Q-}s|13?4S~gHnP*HhY&E{6Ju{l7`BY!(NnRVa3eF_&YuwZD(GG2nY zS`T(cDK%6o8I03@vDmkN|6W1CoiDw=ox*_&r{f4$PdH%N5W2(F+uO#3JAzrq+&qR~ zHD>&6=IO|&sBMhI?|i;hych9Zo#h^6nxduno$Ijmcj4Jkl^g#tF|p6{^EV?SsbBq_ zFP!bq+cmN*Ri6C#vEbLS*E}b@zt(*yv$b?_i8=nshumYjGZ>qAWLaD*Rx%D7*MDo{ z_t2|rV(jef42+Dy$H)A4wUX-?=CGX%f=>6!AnC)&An&O73h1e&Ys&zOS{j^%q&&ZiZU@NsZ^-& z^qeTA`|=lccQ?1a*yy1 zY(pWF%G;h^UiQmh+V5s$^i=MP;npxS;|f@AKiD+0LcF{$QR73>YLllU`Juyyvx|#G zn4*-*-6mBkr#yF}d{{d=Y8Dz4T{6*#a9emMWcX?{l*4Ea3nV;7P3k!Q-;DW|s_R>3=l9pA$Up_I≻@% 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 JJ$3QM{{!73FL(d| literal 0 HcmV?d00001 diff --git a/doc/surya/surya_report/surya_report_DocumentEngine.sol.md b/doc/surya/surya_report/surya_report_DocumentEngine.sol.md index 69a3c95..ed1d0bc 100644 --- a/doc/surya/surya_report/surya_report_DocumentEngine.sol.md +++ b/doc/surya/surya_report/surya_report_DocumentEngine.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| ./DocumentEngine.sol | [object Promise] | +| ./DocumentEngine.sol | d031f83714aa8dd23df6cc3fea3071829fcacf82 | ### Contracts Description Table @@ -15,23 +15,11 @@ |:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| | └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | |||||| -| **DocumentEngine** | Implementation | IERC1643, DocumentEngineInvariant, AccessControl, ERC2771Context ||| +| **DocumentEngine** | Implementation | TokenBindingModule, VersionModule, AccessControlEnumerable, ERC2771Context ||| | └ | | Public ❗️ | 🛑 | ERC2771Context | -| └ | setDocument | Public ❗️ | 🛑 | onlyRole | -| └ | removeDocument | External ❗️ | 🛑 | onlyRole | -| └ | batchSetDocuments | External ❗️ | 🛑 | onlyRole | -| └ | batchSetDocuments | External ❗️ | 🛑 | onlyRole | -| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyRole | -| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyRole | -| └ | getDocument | External ❗️ | |NO❗️ | -| └ | getDocument | External ❗️ | |NO❗️ | -| └ | getAllDocuments | External ❗️ | |NO❗️ | -| └ | getAllDocuments | External ❗️ | |NO❗️ | +| └ | _authorizeDocumentManagement | Internal 🔒 | | | | └ | hasRole | Public ❗️ | |NO❗️ | -| └ | _getDocument | Internal 🔒 | | | -| └ | _removeDocumentName | Internal 🔒 | 🛑 | | -| └ | _removeDocument | Internal 🔒 | 🛑 | | -| └ | _setDocument | Internal 🔒 | 🛑 | | +| └ | supportsInterface | Public ❗️ | |NO❗️ | | └ | _msgSender | Internal 🔒 | | | | └ | _msgData | Internal 🔒 | | | | └ | _contextSuffixLength | Internal 🔒 | | | diff --git a/doc/surya/surya_report/surya_report_DocumentEngineBase.sol.md b/doc/surya/surya_report/surya_report_DocumentEngineBase.sol.md new file mode 100644 index 0000000..7569255 --- /dev/null +++ b/doc/surya/surya_report/surya_report_DocumentEngineBase.sol.md @@ -0,0 +1,44 @@ +## Sūrya's Description Report + +### Files Description Table + + +| File Name | SHA-1 Hash | +|-------------|--------------| +| ./DocumentEngineBase.sol | 30ea1b25c7af9e478f0dbb2b1e984672c10e5a39 | + + +### Contracts Description Table + + +| Contract | Type | Bases | | | +|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| +| └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | +|||||| +| **DocumentEngineBase** | Implementation | IERC1643, IERC1643MultiDocument, DocumentEngineInvariant, Context ||| +| └ | _authorizeDocumentManagement | Internal 🔒 | | | +| └ | _authorizeBoundTokenDocumentManagement | Internal 🔒 | | | +| └ | setDocument | Public ❗️ | 🛑 | onlyDocumentManager | +| └ | removeDocument | External ❗️ | 🛑 | onlyDocumentManager | +| └ | setDocument | External ❗️ | 🛑 | onlyBoundToken | +| └ | removeDocument | External ❗️ | 🛑 | onlyBoundToken | +| └ | batchSetDocuments | External ❗️ | 🛑 | onlyDocumentManager | +| └ | batchSetDocuments | External ❗️ | 🛑 | onlyDocumentManager | +| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyDocumentManager | +| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyDocumentManager | +| └ | getDocument | External ❗️ | |NO❗️ | +| └ | getDocument | External ❗️ | |NO❗️ | +| └ | getAllDocuments | External ❗️ | |NO❗️ | +| └ | getAllDocuments | External ❗️ | |NO❗️ | +| └ | _getDocument | Internal 🔒 | | | +| └ | _removeDocumentName | Internal 🔒 | 🛑 | | +| └ | _removeDocument | Internal 🔒 | 🛑 | | +| └ | _setDocument | Internal 🔒 | 🛑 | | + + +### Legend + +| Symbol | Meaning | +|:--------:|-----------| +| 🛑 | Function can modify state | +| 💵 | Function is payable | diff --git a/doc/surya/surya_report/surya_report_DocumentEngineInvariant.sol.md b/doc/surya/surya_report/surya_report_DocumentEngineInvariant.sol.md index c310e6e..bb74e0f 100644 --- a/doc/surya/surya_report/surya_report_DocumentEngineInvariant.sol.md +++ b/doc/surya/surya_report/surya_report_DocumentEngineInvariant.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| ./DocumentEngineInvariant.sol | [object Promise] | +| ./DocumentEngineInvariant.sol | 5c2227d3902c20320c2a74588eebaf8f5b562a33 | ### Contracts Description Table diff --git a/doc/surya/surya_report/surya_report_DocumentEngineOwnable.sol.md b/doc/surya/surya_report/surya_report_DocumentEngineOwnable.sol.md new file mode 100644 index 0000000..0623991 --- /dev/null +++ b/doc/surya/surya_report/surya_report_DocumentEngineOwnable.sol.md @@ -0,0 +1,32 @@ +## Sūrya's Description Report + +### Files Description Table + + +| File Name | SHA-1 Hash | +|-------------|--------------| +| ./DocumentEngineOwnable.sol | fd190b2e8e884a3940406385ad7cdd22381304f5 | + + +### Contracts Description Table + + +| Contract | Type | Bases | | | +|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| +| └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | +|||||| +| **DocumentEngineOwnable** | Implementation | TokenBindingModule, VersionModule, Ownable2Step, ERC2771Context ||| +| └ | | Public ❗️ | 🛑 | Ownable ERC2771Context | +| └ | _authorizeDocumentManagement | Internal 🔒 | | | +| └ | supportsInterface | Public ❗️ | |NO❗️ | +| └ | _msgSender | Internal 🔒 | | | +| └ | _msgData | Internal 🔒 | | | +| └ | _contextSuffixLength | Internal 🔒 | | | + + +### Legend + +| Symbol | Meaning | +|:--------:|-----------| +| 🛑 | Function can modify state | +| 💵 | Function is payable | diff --git a/doc/surya/surya_report/surya_report_IERC1643MultiDocument.sol.md b/doc/surya/surya_report/surya_report_IERC1643MultiDocument.sol.md new file mode 100644 index 0000000..603b9a2 --- /dev/null +++ b/doc/surya/surya_report/surya_report_IERC1643MultiDocument.sol.md @@ -0,0 +1,30 @@ +## Sūrya's Description Report + +### Files Description Table + + +| File Name | SHA-1 Hash | +|-------------|--------------| +| ./interfaces/IERC1643MultiDocument.sol | 1837f12f2b88fb4a93bb0100608e8e380c75582a | + + +### Contracts Description Table + + +| Contract | Type | Bases | | | +|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| +| └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | +|||||| +| **IERC1643MultiDocument** | Interface | ||| +| └ | getDocument | External ❗️ | |NO❗️ | +| └ | getAllDocuments | External ❗️ | |NO❗️ | +| └ | setDocument | External ❗️ | 🛑 |NO❗️ | +| └ | removeDocument | External ❗️ | 🛑 |NO❗️ | + + +### Legend + +| Symbol | Meaning | +|:--------:|-----------| +| 🛑 | Function can modify state | +| 💵 | Function is payable | diff --git a/doc/surya/surya_report/surya_report_IERC8303.sol.md b/doc/surya/surya_report/surya_report_IERC8303.sol.md new file mode 100644 index 0000000..7ba8997 --- /dev/null +++ b/doc/surya/surya_report/surya_report_IERC8303.sol.md @@ -0,0 +1,27 @@ +## Sūrya's Description Report + +### Files Description Table + + +| File Name | SHA-1 Hash | +|-------------|--------------| +| ./interfaces/IERC8303.sol | d4d17c6161ae92f56abcd7f6b80ea7a5cc6bd99e | + + +### Contracts Description Table + + +| Contract | Type | Bases | | | +|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| +| └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | +|||||| +| **IERC8303** | Interface | ||| +| └ | version | External ❗️ | |NO❗️ | + + +### Legend + +| Symbol | Meaning | +|:--------:|-----------| +| 🛑 | Function can modify state | +| 💵 | Function is payable | diff --git a/doc/surya/surya_report/surya_report_ITokenBinding.sol.md b/doc/surya/surya_report/surya_report_ITokenBinding.sol.md new file mode 100644 index 0000000..e9d97eb --- /dev/null +++ b/doc/surya/surya_report/surya_report_ITokenBinding.sol.md @@ -0,0 +1,29 @@ +## Sūrya's Description Report + +### Files Description Table + + +| File Name | SHA-1 Hash | +|-------------|--------------| +| ./interfaces/ITokenBinding.sol | b1c29dd6482284b8dbdc9d725891ce63edd7093b | + + +### Contracts Description Table + + +| Contract | Type | Bases | | | +|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| +| └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | +|||||| +| **ITokenBinding** | Interface | ||| +| └ | bindToken | External ❗️ | 🛑 |NO❗️ | +| └ | unbindToken | External ❗️ | 🛑 |NO❗️ | +| └ | isTokenBound | External ❗️ | |NO❗️ | + + +### Legend + +| Symbol | Meaning | +|:--------:|-----------| +| 🛑 | Function can modify state | +| 💵 | Function is payable | diff --git a/doc/surya/surya_report/surya_report_TokenBindingModule.sol.md b/doc/surya/surya_report/surya_report_TokenBindingModule.sol.md new file mode 100644 index 0000000..3bfbeac --- /dev/null +++ b/doc/surya/surya_report/surya_report_TokenBindingModule.sol.md @@ -0,0 +1,32 @@ +## Sūrya's Description Report + +### Files Description Table + + +| File Name | SHA-1 Hash | +|-------------|--------------| +| ./modules/TokenBindingModule.sol | e6198bc6b9cc314809dde5b6d237afb82396f7a2 | + + +### Contracts Description Table + + +| Contract | Type | Bases | | | +|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| +| └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | +|||||| +| **TokenBindingModule** | Implementation | DocumentEngineBase, ITokenBinding ||| +| └ | bindToken | External ❗️ | 🛑 |NO❗️ | +| └ | unbindToken | External ❗️ | 🛑 |NO❗️ | +| └ | _setTokenBinding | Internal 🔒 | 🛑 | | +| └ | isTokenBound | Public ❗️ | |NO❗️ | +| └ | _authorizeBoundTokenDocumentManagement | Internal 🔒 | | | +| └ | _checkTokenBound | Internal 🔒 | | | + + +### Legend + +| Symbol | Meaning | +|:--------:|-----------| +| 🛑 | Function can modify state | +| 💵 | Function is payable | diff --git a/doc/surya/surya_report/surya_report_VersionModule.sol.md b/doc/surya/surya_report/surya_report_VersionModule.sol.md new file mode 100644 index 0000000..96dc2da --- /dev/null +++ b/doc/surya/surya_report/surya_report_VersionModule.sol.md @@ -0,0 +1,28 @@ +## Sūrya's Description Report + +### Files Description Table + + +| File Name | SHA-1 Hash | +|-------------|--------------| +| ./modules/VersionModule.sol | 50457b3210365f6d56bfb89d8421dd75464409e2 | + + +### Contracts Description Table + + +| Contract | Type | Bases | | | +|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| +| └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | +|||||| +| **VersionModule** | Implementation | IERC8303, ERC165 ||| +| └ | version | Public ❗️ | |NO❗️ | +| └ | supportsInterface | Public ❗️ | |NO❗️ | + + +### Legend + +| Symbol | Meaning | +|:--------:|-----------| +| 🛑 | Function can modify state | +| 💵 | Function is payable | From 6b8d7d59813fa4567ed3d3404dcab5806f570930 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:04:01 +0200 Subject: [PATCH 29/47] docs: add first Slither run for v0.4.0, refresh Aderyn against CMTAT rc3 / OZ v5.7.0 --- AGENTS.md | 5 +- CLAUDE.md | 5 +- README.md | 13 +++- doc/audits/AUDIT_OVERVIEW.md | 20 +++++- .../v0.4.0/aderyn/aderyn-report-feedback.md | 38 +++++++--- .../tools/v0.4.0/aderyn/aderyn-report.md | 48 +++++++------ .../v0.4.0/slither/slither-report-feedback.md | 69 +++++++++++++++++++ .../tools/v0.4.0/slither/slither-report.md | 67 ++++++++++++++++++ 8 files changed, 226 insertions(+), 39 deletions(-) create mode 100644 doc/audits/tools/v0.4.0/slither/slither-report-feedback.md create mode 100644 doc/audits/tools/v0.4.0/slither/slither-report.md diff --git a/AGENTS.md b/AGENTS.md index f2bf9d7..0e7ccba 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -156,7 +156,10 @@ Other important files: `doc/audits/` — the security overview (`AUDIT_OVERVIEW.md`) plus versioned static-analysis output under `doc/audits/tools/vX.Y.Z//`, each with a `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging - every finding. Aderyn was run for `v0.4.0`; Slither has never been run here. + every finding. Both Aderyn `0.6.5` (0 High · 6 Low) and Slither `0.11.5` + (0 High · 1 Med · 1 Low · 2 Info) were run for `v0.4.0` — nothing to fix in either. + Slither's dependency filter must be `lib` (Foundry layout); `--filter-paths` fails + open, so an entry matching nothing silently pulls the vendored tree into scope. - `IMPROVEMENT.md` — the open items: deviations from the two ERC specifications, with severity, effort and a recommendation for each. Update it when an item is fixed (move the record to `CHANGELOG.md` and `doc/audits/AUDIT_OVERVIEW.md`) diff --git a/CLAUDE.md b/CLAUDE.md index f2bf9d7..0e7ccba 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -156,7 +156,10 @@ Other important files: `doc/audits/` — the security overview (`AUDIT_OVERVIEW.md`) plus versioned static-analysis output under `doc/audits/tools/vX.Y.Z//`, each with a `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging - every finding. Aderyn was run for `v0.4.0`; Slither has never been run here. + every finding. Both Aderyn `0.6.5` (0 High · 6 Low) and Slither `0.11.5` + (0 High · 1 Med · 1 Low · 2 Info) were run for `v0.4.0` — nothing to fix in either. + Slither's dependency filter must be `lib` (Foundry layout); `--filter-paths` fails + open, so an entry matching nothing silently pulls the vendored tree into scope. - `IMPROVEMENT.md` — the open items: deviations from the two ERC specifications, with severity, effort and a recommendation for each. Update it when an item is fixed (move the record to `CHANGELOG.md` and `doc/audits/AUDIT_OVERVIEW.md`) diff --git a/README.md b/README.md index 9aa13c2..d694199 100644 --- a/README.md +++ b/README.md @@ -466,17 +466,24 @@ finding against the source. The security overview is | Release | Tool | Result | Report | Triage | | ------- | ---- | ------ | ------ | ------ | | v0.4.0 | Aderyn `0.6.5` | 0 High · 6 Low — **nothing to fix** | [report](./doc/audits/tools/v0.4.0/aderyn/aderyn-report.md) | [feedback](./doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md) | -| v0.4.0 | Slither | not run | — | — | +| v0.4.0 | Slither `0.11.5` | 0 High · 1 Medium · 1 Low · 2 Info — **nothing to fix** | [report](./doc/audits/tools/v0.4.0/slither/slither-report.md) | [feedback](./doc/audits/tools/v0.4.0/slither/slither-report-feedback.md) | ```bash # Aderyn — mocks excluded (this project's mocks live in test/, which Aderyn does not scan) aderyn -x mocks --output doc/audits/tools/v0.4.0/aderyn/aderyn-report.md -# Slither -slither . --checklist --filter-paths "node_modules,test,forge-std,CMTAT,openzeppelin-contracts" \ +# Slither — mocks excluded (they live in test/, removed by the `test` filter) +slither . --checklist --filter-paths "node_modules,lib,test,forge-std,mocks" \ > doc/audits/tools/v0.4.0/slither/slither-report.md ``` +> **Filter on `lib`, not on individual submodule names.** This is a Foundry project, so every +> dependency lives under `lib/`. `--filter-paths` fails *open* — an entry matching nothing silently +> widens scope instead of erroring — so naming submodules one by one risks pulling a whole vendored +> tree into the report. Verify with `grep -c 'lib/\|node_modules/' `, which must return `0`. +> Slither also writes its checklist to **stdout** and its detector log to **stderr**, and exits +> non-zero when it finds anything: `exit=255` with a populated report is the normal outcome. + > **Static-analysis output is leads, not findings.** Every dismissal in the feedback files was > verified against the cited `file:line`, and neither tool can see the specification-level issues > that matter most here — those are in [`IMPROVEMENT.md`](./IMPROVEMENT.md). diff --git a/doc/audits/AUDIT_OVERVIEW.md b/doc/audits/AUDIT_OVERVIEW.md index 94f975b..89f728d 100644 --- a/doc/audits/AUDIT_OVERVIEW.md +++ b/doc/audits/AUDIT_OVERVIEW.md @@ -7,7 +7,7 @@ ## In scope -The `src/` tree only — 9 files, 298 nSLOC as of `v0.4.0`: +The `src/` tree only — 9 files, 307 nSLOC as of `v0.4.0`: ``` src/DocumentEngine.sol src/interfaces/IERC1643MultiDocument.sol @@ -25,18 +25,32 @@ Out of scope: `lib/` (CMTAT, RuleEngine, OpenZeppelin — audited, or not, upstr | Analysis | Version | Report | Triage | | --- | --- | --- | --- | | Aderyn `0.6.5` | `v0.4.0` | [report](./tools/v0.4.0/aderyn/aderyn-report.md) | [feedback](./tools/v0.4.0/aderyn/aderyn-report-feedback.md) | -| Slither | — | not run | — | +| Slither `0.11.5` | `v0.4.0` | [report](./tools/v0.4.0/slither/slither-report.md) | [feedback](./tools/v0.4.0/slither/slither-report-feedback.md) | | ERC conformance analysis (AI-assisted) | `v0.4.0` | open items: [`IMPROVEMENT.md`](../../IMPROVEMENT.md) | — | +Both tool runs are against CMTAT `v3.3.0-rc3` and OpenZeppelin `v5.7.0`, with mocks and tests +excluded. + ## Static-analysis results | Tool | High | Medium | Low | Info | Anything to fix? | | --- | --- | --- | --- | --- | --- | | Aderyn `0.6.5` | 0 | — | 6 | 0 | **No.** 4 by design, 1 environment, 1 false positive; 1 of the "by design" instances overlaps a known scalability item (§4.7) | -| Slither | — | — | — | — | not run for `v0.4.0` | +| Slither `0.11.5` | 0 | 1 | 1 | 2 | **No.** All 4 are false positives, reducing to two pieces of code: an existence check (`doc.lastModified == 0`) read as a timestamp comparison, and two `_msgData()` overrides read as dead code | Aderyn reports no Medium or Info categories; it classifies only High and Low. +**Neither tool found anything to fix in `v0.4.0`.** The two agree on the absence of the classic +classes — no reentrancy, no access-control gap, no uninitialised state, no unchecked external call — +which is the expected result for a contract that holds no funds and makes no external calls. They +disagree only on what is worth reporting: Slither's highest result (`incorrect-equality`, Medium) is +one Aderyn ignores, and Aderyn's loop advisories draw nothing from Slither. Each dismissal was +verified against the cited line; the `_msgData()` "dead code" was verified by deleting it and +confirming the compile fails (`Error (6480): Derived contract must override function "_msgData"`). + +Note the standing limitation: neither tool can see the specification-level issues that matter most +for this engine — those are tracked as open items below. + ## Substantive findings fixed in `v0.4.0` From the ERC conformance analysis (open items: [`IMPROVEMENT.md`](../../IMPROVEMENT.md)) rather than from the diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md index 3b7a940..dfddfab 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md @@ -6,8 +6,9 @@ | Command | `aderyn -x mocks --output doc/audits/tools/v0.4.0/aderyn/aderyn-report.md` | | Tool version | `aderyn 0.6.5` | | Scope | `src/` only — 9 files, 307 nSLOC. **Mocks and tests excluded.** This project keeps its mocks (`CMTATDocumentEngineMock`, `OpenDocumentEngine`) inside `test/DocumentEngine.t.sol`, which Aderyn does not scan, so `-x mocks` matched nothing and changed nothing. | -| Dependency | CMTAT `v3.3.0-rc2` (`35d8940b`) | +| Dependencies | CMTAT `v3.3.0-rc3` (`658672f1`), OpenZeppelin `v5.7.0` (`cab19933`) | | Result | **0 High · 6 Low** | +| Companion | [`../slither/slither-report.md`](../slither/slither-report.md) — Slither `0.11.5`, 4 results, also nothing to fix | ## Executive triage @@ -31,7 +32,7 @@ CMTAT upgrade. | ID | Detector | Sev | Instances | Disposition | Reason (verified against the cited lines) | | --- | --- | --- | --- | --- | --- | | L-1 | Centralization Risk | Low | 2 | **By design** | `DocumentEngine.sol:24`, `DocumentEngineOwnable.sol:24`. The whole premise of the contract is that a trusted operator manages documents for a fleet of subjects; `DOCUMENT_MANAGER_ROLE` (and `owner`) are that operator. Documented in the README and analysed in `IMPROVEMENT.md` item 1, which concludes the global role is the correct model for the single-issuer fleet this engine targets. Aderyn cannot express that distinction. | -| L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** (floor since raised) | Every file uses a caret pragma, intentionally, so the sources stay consumable as a library by projects on a different `0.8.x`; the compiler actually used for the deployed bytecode is pinned to `0.8.34` in `foundry.toml`, and `foundry.lock` pins every dependency. Verified: no file uses a construct that behaves differently across the allowed range. **Update (post-run):** the floor this report saw, `^0.8.20`, over-promised once CMTAT `v3.3.0-rc3` moved `draft-IERC1643.sol` to `^0.8.24` — `0.8.20`–`0.8.23` could not in fact compile the tree (`AccessControlEnumerable.sol` and `EnumerableSet.sol` were already `^0.8.24`). Every file is now `^0.8.24`, which is the true `src/` floor; the full project including the CMTAT-importing tests needs `0.8.27`, because `require(cond, CustomError())` is legacy-pipeline-only from that version on. | +| L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** | Every file uses a caret pragma, intentionally, so the sources stay consumable as a library by projects on a different `0.8.x`; the compiler actually used for the deployed bytecode is pinned to `0.8.34` in `foundry.toml`, and `foundry.lock` pins every dependency. Verified: no file uses a construct that behaves differently across the allowed range. The floor is now **`^0.8.24`**, raised from `^0.8.20` after the previous run: `^0.8.20` over-promised, because `AccessControlEnumerable.sol` and `EnumerableSet.sol` were already `^0.8.24` and CMTAT `v3.3.0-rc3` moved `draft-IERC1643.sol` there too — no compiler in `0.8.20`–`0.8.23` could build the tree. `0.8.24` is the true `src/` floor; the full project including the CMTAT-importing tests needs `0.8.27`, because `require(cond, CustomError())` is legacy-pipeline-only from that version on. | | L-3 | PUSH0 Opcode | Low | 9 | **Environment** | Consequence of the caret pragma plus `evm_version = prague`: the compiler emits `PUSH0`, which is unavailable on chains that have not adopted Shanghai. Not a source defect. A deployer targeting such a chain must lower `evm_version` in `foundry.toml` — but CMTAT v3 itself requires `prague`, so that configuration is out of scope for this engine. | | L-4 | Loop Contains `require`/`revert` | Low | 4 | **By design** | `DocumentEngineBase.sol:124, 142, 156, 170` — the four batch loops. The reverts are raised inside `_setDocument` / `_removeDocument` (`ERC1643InvalidName`, `MultiDocumentInvalidSubject`, `ERC1643MissingDocument`). Batch operations are deliberately **all-or-nothing**: a batch containing one bad entry must not half-apply, since partial application would leave the operator unable to tell which documents were written without re-reading every entry. Skipping bad entries instead would silently drop them. | | L-5 | Costly operations inside loop | Low | 5 | **By design** ×4, **known item** ×1 | Four instances (`:124, 142, 156, 170`) are storage writes in the batch loops — unavoidable, and the reason the batch functions exist is to amortise the 21 000-gas transaction overhead across those writes. The fifth (`:238`) is `_removeDocumentName`'s linear scan with swap-and-pop; see the triage note above and `IMPROVEMENT.md` item 4. | @@ -48,13 +49,32 @@ Worth noting explicitly, since it is a null result that is easy to misread as "n `TokenBindingModule._setTokenBinding` — which adds a revert and an early return — triggered **no** new finding, including no addition to L-4 (`revert` in a loop), because it contains no loop. +## Delta from the previous run (dependency upgrade) + +Re-run after the `v0.4.0` dependency bump — CMTAT `v3.3.0-rc2` → `v3.3.0-rc3`, OpenZeppelin +`v5.6.1` → `v5.7.0`, and the source pragma `^0.8.20` → `^0.8.24`. + +**Nothing moved.** The same six detectors fire with the same instance counts (2 / 9 / 9 / 4 / 5 / 1), +on the same lines, and nSLOC is unchanged at 307 across the same 9 files. The only textual difference +in the raw report is the pragma quoted under L-2 and L-3, which now reads `^0.8.24`. + +Two null results worth recording, because they are easy to misread as "not analysed": + +- **The pragma bump did not clear L-2 or L-3.** Aderyn flags the *caret*, not the floor, so raising + `^0.8.20` to `^0.8.24` leaves both counts at 9. L-3's own description still names `0.8.20` — that + is boilerplate detector text, not a reading of the current source. +- **The OpenZeppelin `EnumerableSet.at()` → `pos()` deprecation produced no finding.** This engine + has no call site of either, and its only exposure is the inherited + `AccessControlEnumerable.getRoleMember`, whose behaviour is unchanged. + ## Delta from the previous version -None — this is the **first** static-analysis run recorded for this repository. `doc/audits/` did not -exist before `v0.4.0`; the `CHANGELOG.md` release checklist referenced `doc/audits/tools` but no -report had been committed. There is therefore no baseline to diff against, and future runs should -diff against this one. +None — `v0.4.0` is the **first** release with static analysis recorded. `doc/audits/` did not exist +before it. Future runs should diff against this one. -Note for the next run: the `CLAUDE.md` file tree claims a Slither report exists under `doc/`. It does -not. Slither is installed (`slither --version` resolves) and was **not** run for `v0.4.0` — this -release re-ran Aderyn only. A Slither run would make the next delta meaningful across both tools. +**Slither has now been run** (`0.11.5`, 4 results, nothing to fix) — see +[`../slither/slither-report-feedback.md`](../slither/slither-report-feedback.md). This closes the +gap flagged here previously, so the next release can diff both tools. The two disagree on what is +worth reporting: Slither raised an existence-check equality and two required `_msgData()` overrides +that Aderyn ignored, while Aderyn's loop advisories (L-4, L-5) and `_grantRole` return (L-6) drew +nothing from Slither. No finding from either tool is real. diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md index a47629d..489fdef 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md @@ -1,4 +1,4 @@ -> **Summary — generated for DocumentEngine `v0.4.0` (CMTAT `v3.3.0-rc2`).** +> **Summary — generated for DocumentEngine `v0.4.0` (CMTAT `v3.3.0-rc3`, OpenZeppelin `v5.7.0`).** > > | | | > | --- | --- | @@ -6,19 +6,23 @@ > | Tool version | `aderyn 0.6.5` | > | Scope | `src/` only — 9 files, 307 nSLOC. **Mocks/tests excluded** (this project has no `src/mocks`; its mocks live in `test/`, which Aderyn does not scan). | > | Result | **0 High · 6 Low · 0 Info** | -> | Verdict | **Nothing to fix.** No finding is exploitable. One (L-5 at `DocumentEngineBase.sol:238`) independently corroborates a known gas/scalability item already tracked as [`IMPROVEMENT.md`](../../../../IMPROVEMENT.md) item 4. | +> | Verdict | **Nothing to fix.** No finding is exploitable. One (L-5 at `DocumentEngineBase.sol:238`) independently corroborates a known gas/scalability item already tracked as `IMPROVEMENT.md` item 4. | > > | ID | Detector | Sev | Instances | Assessment | > | --- | --- | --- | --- | --- | > | L-1 | Centralization Risk | Low | 2 | **By design** — a document manager is a privileged operator by definition | -> | L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** — the caret is deliberate; the deployed compiler is pinned in `foundry.toml`. The `^0.8.20` floor seen by this run has since been raised to `^0.8.24` — see the feedback file | +> | L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** — the caret is deliberate; the deployed compiler is pinned in `foundry.toml`. Now `^0.8.24`, the true `src/` floor | > | L-3 | PUSH0 Opcode | Low | 9 | **Environment** — `evm_version = prague`; only relevant on chains without PUSH0 | > | L-4 | Loop Contains `require`/`revert` | Low | 4 | **By design** — batch operations are deliberately all-or-nothing | -> | L-5 | Costly operations inside loop | Low | 5 | **By design** (4 batch loops) + **1 known item** — `_removeDocumentName` is O(n), see §4.7 | +> | L-5 | Costly operations inside loop | Low | 5 | **By design** (4 batch loops) + **1 known item** — `_removeDocumentName` is O(n), see `IMPROVEMENT.md` item 4 | > | L-6 | Unchecked Return | Low | 1 | **False positive** — `_grantRole` in a constructor on a fresh contract cannot return `false` | > +> Unchanged from the previous `v0.4.0` run (CMTAT `v3.3.0-rc2`, OZ `v5.6.1`): same six detectors, +> same instance counts, same lines. See the feedback file's Delta section. +> > Full triage, with the reasoning verified against each cited line: > [`aderyn-report-feedback.md`](./aderyn-report-feedback.md). +> Companion Slither run: [`../slither/slither-report.md`](../slither/slither-report.md). > Security overview: [`doc/audits/AUDIT_OVERVIEW.md`](../../../AUDIT_OVERVIEW.md). # Aderyn Analysis Report @@ -108,55 +112,55 @@ Consider using a specific version of Solidity in your contracts instead of a wid - Found in src/DocumentEngine.sol [Line: 2](../../../../../src/DocumentEngine.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/DocumentEngineBase.sol [Line: 2](../../../../../src/DocumentEngineBase.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/DocumentEngineInvariant.sol [Line: 2](../../../../../src/DocumentEngineInvariant.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/DocumentEngineOwnable.sol [Line: 2](../../../../../src/DocumentEngineOwnable.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/interfaces/IERC1643MultiDocument.sol [Line: 2](../../../../../src/interfaces/IERC1643MultiDocument.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/interfaces/IERC8303.sol [Line: 2](../../../../../src/interfaces/IERC8303.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/interfaces/ITokenBinding.sol [Line: 2](../../../../../src/interfaces/ITokenBinding.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/modules/TokenBindingModule.sol [Line: 2](../../../../../src/modules/TokenBindingModule.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/modules/VersionModule.sol [Line: 2](../../../../../src/modules/VersionModule.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` @@ -173,55 +177,55 @@ Solc compiler version 0.8.20 switches the default target EVM version to Shanghai - Found in src/DocumentEngine.sol [Line: 2](../../../../../src/DocumentEngine.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/DocumentEngineBase.sol [Line: 2](../../../../../src/DocumentEngineBase.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/DocumentEngineInvariant.sol [Line: 2](../../../../../src/DocumentEngineInvariant.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/DocumentEngineOwnable.sol [Line: 2](../../../../../src/DocumentEngineOwnable.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/interfaces/IERC1643MultiDocument.sol [Line: 2](../../../../../src/interfaces/IERC1643MultiDocument.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/interfaces/IERC8303.sol [Line: 2](../../../../../src/interfaces/IERC8303.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/interfaces/ITokenBinding.sol [Line: 2](../../../../../src/interfaces/ITokenBinding.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/modules/TokenBindingModule.sol [Line: 2](../../../../../src/modules/TokenBindingModule.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` - Found in src/modules/VersionModule.sol [Line: 2](../../../../../src/modules/VersionModule.sol#L2) ```solidity - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; ``` diff --git a/doc/audits/tools/v0.4.0/slither/slither-report-feedback.md b/doc/audits/tools/v0.4.0/slither/slither-report-feedback.md new file mode 100644 index 0000000..7c1d254 --- /dev/null +++ b/doc/audits/tools/v0.4.0/slither/slither-report-feedback.md @@ -0,0 +1,69 @@ +# Slither report — triage (DocumentEngine `v0.4.0`) + +| | | +| --- | --- | +| Report | [`slither-report.md`](./slither-report.md) | +| Command | `slither . --checklist --filter-paths "node_modules,lib,test,forge-std,mocks"` | +| Tool version | `slither 0.11.5` | +| Scope | `src/` only — 28 contracts analysed with 101 detectors. **Mocks and tests excluded.** This project keeps its mocks (`CMTATDocumentEngineMock`, `OpenDocumentEngine`) inside `test/DocumentEngine.t.sol`, which the `test` filter removes; there is no `src/mocks`, so the `mocks` filter entry matched nothing. | +| Dependencies | CMTAT `v3.3.0-rc3` (`658672f1`), OpenZeppelin `v5.7.0` (`cab19933`) | +| Result | **0 High · 1 Medium · 1 Low · 2 Informational** (4 results) | + +## Executive triage + +**Nothing to fix.** No finding is exploitable, and none blocks the `v0.4.0` release. + +All four results reduce to two underlying pieces of code, and both are correct as written: + +- **Two detectors (`incorrect-equality`, `timestamp`) fire on the same line** — + `DocumentEngineBase.sol:250`, `doc.lastModified == 0`. Both misread an *existence sentinel* as a + *time comparison*. See below; neither detector has a notion of "zero means absent". +- **`dead-code` ×2** flags the `_msgData()` overrides. These are not dead — Solidity **requires** + them. Verified by deleting one and compiling: `Error (6480): Derived contract must override + function "_msgData". Two or more base classes define function with same name and parameter types.` + +The Medium severity on `incorrect-equality` deserves a word, because it is the highest-severity +result either tool produced for this release and it is worth being explicit that it is not real. +Slither's detector targets strict equality against a *quantity that can step past the compared +value* — a balance that can be donated to, or a timestamp compared with `==` where a block can skip +the exact second. Neither shape applies here: `0` is not a point on a timeline the value passes +through, it is the default of an unwritten struct. + +## Findings + +| ID | Detector | Sev | Conf | Instances | Disposition | Reason (verified against the cited lines) | +| --- | --- | --- | --- | --- | --- | --- | +| ID-0 | `incorrect-equality` | Medium | High | 1 | **False positive** | `DocumentEngineBase.sol:250`, inside `_removeDocument`: `if (doc.lastModified == 0) revert ERC1643MissingDocument();`. `lastModified` is only ever assigned `block.timestamp` (`:282`), which is non-zero on every live chain, so a *stored* document can never read back as `0`. The comparison is therefore a total existence test — the same idiom `_setDocument` uses at `:276` to detect a new name. The ERC-1643 spec requires the revert-on-missing behaviour this line implements. Making it `<= 0` or a range check, as the detector suggests, would change nothing and read worse. Covered by `testCannotRemoveMissingDocument` (`test/DocumentEngine.t.sol:450`). | +| ID-1 | `timestamp` | Low | Medium | 1 | **False positive** | Same line as ID-0. The detector flags any use of a timestamp in a comparison, on the theory that a validator can nudge `block.timestamp` by a few seconds and flip a branch. There is no ordering comparison here — no `<`, `>`, or deadline — only equality against the `0` sentinel. A validator cannot set `block.timestamp` to `0`, so no achievable manipulation changes which branch is taken. The stored value is metadata surfaced by `getDocument`; nothing in the engine makes a decision based on how recent it is. | +| ID-2 | `dead-code` | Info | Medium | 1 | **False positive — required override** | `DocumentEngine.sol:118-120`, `_msgData()`. `DocumentEngine` inherits `Context` through two paths (`AccessControlEnumerable` → `AccessControl` → `Context`, and `ERC2771Context` → `Context`), and `ERC2771Context` overrides `_msgData()`. Solidity therefore demands an explicit `override(ERC2771Context, Context)` in the derived contract. **Verified empirically:** removing the function fails to compile with `Error (6480): Derived contract must override function "_msgData"`. Slither reports it "never used" because nothing in this project calls `_msgData()` directly — but it is what makes ERC-2771 calldata handling correct for any inherited code that does. | +| ID-3 | `dead-code` | Info | Medium | 1 | **False positive — required override** | `DocumentEngineOwnable.sol:68-70`. Identical to ID-2, via `Ownable2Step` → `Ownable` → `Context` and `ERC2771Context` → `Context`. | + +## What Slither did *not* flag + +Worth recording, since absences are easy to misread as "not analysed". With 101 detectors over the +full `src/` tree, Slither reported **no** reentrancy, no access-control gap, no uninitialised state, +no unchecked external call, no shadowing, and no arbitrary-`from` issue. That is the expected result +for this contract — the engine holds no funds, makes no external calls, and every state-changing +entry point is behind `onlyDocumentManager` or `onlyBoundToken`. + +Note also that Slither did **not** reproduce Aderyn's L-6 (`_grantRole` return value ignored) or its +loop advisories (L-4, L-5). The two tools disagree on what is worth reporting rather than on the +facts; every one of those is triaged in the Aderyn feedback file. + +## Delta from the previous version + +None — this is the **first** Slither run recorded for this repository. `v0.4.0`'s earlier audit pass +ran Aderyn only, and the `doc/audits/tools/v0.4.0/slither/` directory did not exist. There is no +baseline to diff against; future runs should diff against this one. + +Two notes for whoever runs it next: + +1. **Use `lib` as the dependency filter, not individual submodule names.** This is a Foundry project, + so every dependency lives under `lib/`. The command previously documented in the README — + `--filter-paths "node_modules,test,forge-std,CMTAT,openzeppelin-contracts"` — names submodules + individually and omits `lib/RuleEngine` entirely. Slither's `--filter-paths` fails *open*: an + entry that matches nothing silently widens scope rather than erroring. The README has been + updated to the `lib` form used here. +2. **Slither writes the checklist to stdout and its detector log to stderr, and exits non-zero when + it finds anything.** `exit=255` with a populated report is the normal, successful outcome — do not + read it as a failed run. diff --git a/doc/audits/tools/v0.4.0/slither/slither-report.md b/doc/audits/tools/v0.4.0/slither/slither-report.md new file mode 100644 index 0000000..6555ac8 --- /dev/null +++ b/doc/audits/tools/v0.4.0/slither/slither-report.md @@ -0,0 +1,67 @@ +> **Summary — generated for DocumentEngine `v0.4.0` (CMTAT `v3.3.0-rc3`, OpenZeppelin `v5.7.0`).** +> +> | | | +> | --- | --- | +> | Command | `slither . --checklist --filter-paths "node_modules,lib,test,forge-std,mocks"` | +> | Tool version | `slither 0.11.5` | +> | Scope | `src/` only — 28 contracts analysed with 101 detectors (the count includes inherited OpenZeppelin/CMTAT contracts pulled in by the compiler; findings are filtered to project sources). **Mocks/tests excluded** — this project's mocks (`CMTATDocumentEngineMock`, `OpenDocumentEngine`) live in `test/DocumentEngine.t.sol`, which the `test` filter removes. | +> | Result | **0 High · 1 Medium · 1 Low · 2 Informational** (4 results) | +> | Verdict | **Nothing to fix.** Two false positives on one existence check, and two required Solidity overrides misread as dead code. | +> +> | Detector | Severity | Confidence | Instances | Assessment | +> | --- | --- | --- | --- | --- | +> | `incorrect-equality` | Medium | High | 1 | **False positive** — `doc.lastModified == 0` is an existence sentinel, not a threshold comparison | +> | `timestamp` | Low | Medium | 1 | **False positive** — same line; equality against `0`, no miner-influenceable ordering | +> | `dead-code` | Informational | Medium | 2 | **False positive** — `_msgData()` is a *mandatory* override; removing it fails to compile (verified) | +> +> **Scope check:** `grep -c 'lib/\|node_modules/'` over the tool output below returns **0** — no +> dependency code is in scope. This is a Foundry project, so the dependency filter entry is `lib`; note this +> differs from the command previously documented in the README, which listed individual submodule +> names and would have left `lib/RuleEngine` unfiltered. +> +> Full triage, with the reasoning verified against each cited line: +> [`slither-report-feedback.md`](./slither-report-feedback.md). +> Companion Aderyn run: [`../aderyn/aderyn-report.md`](../aderyn/aderyn-report.md). +> Security overview: [`doc/audits/AUDIT_OVERVIEW.md`](../../../AUDIT_OVERVIEW.md). + +**THIS CHECKLIST IS NOT COMPLETE**. Use `--show-ignored-findings` to show all the results. +Summary + - [incorrect-equality](#incorrect-equality) (1 results) (Medium) + - [timestamp](#timestamp) (1 results) (Low) + - [dead-code](#dead-code) (2 results) (Informational) +## incorrect-equality +Impact: Medium +Confidence: High + - [ ] ID-0 +[DocumentEngineBase._removeDocument(address,bytes32)](src/DocumentEngineBase.sol#L247-L262) uses a dangerous strict equality: + - [doc.lastModified == 0](src/DocumentEngineBase.sol#L250) + +src/DocumentEngineBase.sol#L247-L262 + + +## timestamp +Impact: Low +Confidence: Medium + - [ ] ID-1 +[DocumentEngineBase._removeDocument(address,bytes32)](src/DocumentEngineBase.sol#L247-L262) uses timestamp for comparisons + Dangerous comparisons: + - [doc.lastModified == 0](src/DocumentEngineBase.sol#L250) + +src/DocumentEngineBase.sol#L247-L262 + + +## dead-code +Impact: Informational +Confidence: Medium + - [ ] ID-2 +[DocumentEngine._msgData()](src/DocumentEngine.sol#L118-L120) is never used and should be removed + +src/DocumentEngine.sol#L118-L120 + + + - [ ] ID-3 +[DocumentEngineOwnable._msgData()](src/DocumentEngineOwnable.sol#L68-L70) is never used and should be removed + +src/DocumentEngineOwnable.sol#L68-L70 + + From 68cec2939650a7e6554abfddce7c19b25e43c81e Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:14:58 +0200 Subject: [PATCH 30/47] style: order functions per Solidity style guide, use named imports, complete NatSpec (behaviour-preserving) --- CHANGELOG.md | 9 ++ README.md | 18 +-- .../v0.4.0/aderyn/aderyn-report-feedback.md | 18 ++- .../tools/v0.4.0/aderyn/aderyn-report.md | 32 ++-- .../v0.4.0/slither/slither-report-feedback.md | 8 +- .../tools/v0.4.0/slither/slither-report.md | 24 +-- .../surya_graph_DocumentEngine.sol.png | Bin 118324 -> 116239 bytes .../surya_graph_DocumentEngineBase.sol.png | Bin 154518 -> 155577 bytes .../surya_graph_DocumentEngineOwnable.sol.png | Bin 87312 -> 87322 bytes .../surya_graph_IERC1643MultiDocument.sol.png | Bin 42431 -> 42433 bytes .../surya_report_DocumentEngine.sol.md | 4 +- .../surya_report_DocumentEngineBase.sol.md | 10 +- .../surya_report_DocumentEngineOwnable.sol.md | 4 +- .../surya_report_IERC1643MultiDocument.sol.md | 6 +- .../surya_report_ITokenBinding.sol.md | 2 +- .../surya_report_TokenBindingModule.sol.md | 4 +- .../surya_report_VersionModule.sol.md | 2 +- script/DeployDocumentEngine.s.sol | 14 +- script/DeployDocumentEngineOwnable.s.sol | 14 +- src/DocumentEngine.sol | 63 ++++++-- src/DocumentEngineBase.sol | 147 +++++++++++++----- src/DocumentEngineOwnable.sol | 39 +++-- src/interfaces/IERC1643MultiDocument.sol | 88 +++++++---- src/interfaces/ITokenBinding.sol | 38 +++-- src/modules/TokenBindingModule.sol | 14 +- src/modules/VersionModule.sol | 3 + 26 files changed, 384 insertions(+), 177 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 346d315..f8219a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,15 @@ for which CMTAT release each version of this engine is built against. - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) [`v3.0.0-rc5`](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) - `foundry.lock` now records every submodule by tag; all five entries had gone stale since `v0.3.0`. - **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) +- **Style pass across `src/` and `script/` — behaviour-preserving.** Brought the sources in line with + the Solidity style guide: functions reordered by visibility group (external → public → internal, + `view`/`pure` last within each), so the `_authorize*` hooks and the ERC-2771 context overrides now + follow the public API instead of preceding it; every brace-less global import replaced by a named + one (which required adding the previously implicit `Context` and `AccessControl` imports, since a + named import no longer re-exports a dependency's own imports); and NatSpec completed with a + `@param` per argument and a `@return` per return value. No signature, visibility, body or storage + layout changed — verified by an unchanged per-contract function set, a clean `forge build`, and + 72/72 tests passing. - **Source pragma raised `^0.8.20` → `^0.8.24`** across `src/`, `script/` and `test/`. This is a correction, not a new restriction: `^0.8.20` had become an over-promise, advertising a range the sources could not actually compile in. OpenZeppelin's `AccessControlEnumerable.sol` and diff --git a/README.md b/README.md index d694199..5643344 100644 --- a/README.md +++ b/README.md @@ -305,9 +305,6 @@ each deployment contributes only its access-control layer and its ERC-2771 conte | └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | |||||| | **DocumentEngineBase** | Implementation | IERC1643, IERC1643MultiDocument, DocumentEngineInvariant, Context ||| -| └ | _authorizeDocumentManagement | Internal 🔒 | | | -| └ | _authorizeBoundTokenDocumentManagement | Internal 🔒 | | | -| └ | setDocument | Public ❗️ | 🛑 | onlyDocumentManager | | └ | removeDocument | External ❗️ | 🛑 | onlyDocumentManager | | └ | setDocument | External ❗️ | 🛑 | onlyBoundToken | | └ | removeDocument | External ❗️ | 🛑 | onlyBoundToken | @@ -319,24 +316,27 @@ each deployment contributes only its access-control layer and its ERC-2771 conte | └ | getDocument | External ❗️ | |NO❗️ | | └ | getAllDocuments | External ❗️ | |NO❗️ | | └ | getAllDocuments | External ❗️ | |NO❗️ | -| └ | _getDocument | Internal 🔒 | | | +| └ | setDocument | Public ❗️ | 🛑 | onlyDocumentManager | | └ | _removeDocumentName | Internal 🔒 | 🛑 | | | └ | _removeDocument | Internal 🔒 | 🛑 | | | └ | _setDocument | Internal 🔒 | 🛑 | | +| └ | _authorizeDocumentManagement | Internal 🔒 | | | +| └ | _authorizeBoundTokenDocumentManagement | Internal 🔒 | | | +| └ | _getDocument | Internal 🔒 | | | |||||| | **DocumentEngine** | Implementation | TokenBindingModule, VersionModule, AccessControlEnumerable, ERC2771Context ||| | └ | | Public ❗️ | 🛑 | ERC2771Context | -| └ | _authorizeDocumentManagement | Internal 🔒 | | | | └ | hasRole | Public ❗️ | |NO❗️ | | └ | supportsInterface | Public ❗️ | |NO❗️ | +| └ | _authorizeDocumentManagement | Internal 🔒 | | | | └ | _msgSender | Internal 🔒 | | | | └ | _msgData | Internal 🔒 | | | | └ | _contextSuffixLength | Internal 🔒 | | | |||||| | **DocumentEngineOwnable** | Implementation | TokenBindingModule, VersionModule, Ownable2Step, ERC2771Context ||| | └ | | Public ❗️ | 🛑 | Ownable ERC2771Context | -| └ | _authorizeDocumentManagement | Internal 🔒 | | | | └ | supportsInterface | Public ❗️ | |NO❗️ | +| └ | _authorizeDocumentManagement | Internal 🔒 | | | | └ | _msgSender | Internal 🔒 | | | | └ | _msgData | Internal 🔒 | | | | └ | _contextSuffixLength | Internal 🔒 | | | @@ -344,8 +344,8 @@ each deployment contributes only its access-control layer and its ERC-2771 conte | **TokenBindingModule** | Implementation | DocumentEngineBase, ITokenBinding ||| | └ | bindToken | External ❗️ | 🛑 |NO❗️ | | └ | unbindToken | External ❗️ | 🛑 |NO❗️ | -| └ | _setTokenBinding | Internal 🔒 | 🛑 | | | └ | isTokenBound | Public ❗️ | |NO❗️ | +| └ | _setTokenBinding | Internal 🔒 | 🛑 | | | └ | _authorizeBoundTokenDocumentManagement | Internal 🔒 | | | | └ | _checkTokenBound | Internal 🔒 | | | |||||| @@ -362,10 +362,10 @@ each deployment contributes only its access-control layer and its ERC-2771 conte | └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | |||||| | **IERC1643MultiDocument** | Interface | ||| -| └ | getDocument | External ❗️ | |NO❗️ | -| └ | getAllDocuments | External ❗️ | |NO❗️ | | └ | setDocument | External ❗️ | 🛑 |NO❗️ | | └ | removeDocument | External ❗️ | 🛑 |NO❗️ | +| └ | getDocument | External ❗️ | |NO❗️ | +| └ | getAllDocuments | External ❗️ | |NO❗️ | |||||| | **ITokenBinding** | Interface | ||| | └ | bindToken | External ❗️ | 🛑 |NO❗️ | diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md index dfddfab..01be8a8 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md @@ -17,7 +17,7 @@ Five of the six are the analyzer's standing advisories about deliberate design choices (a privileged operator, a caret pragma, PUSH0, revert-in-loop, storage-writes-in-loop) and one is a false positive. -The one result worth keeping in view is **L-5 at `DocumentEngineBase.sol:238`**, which is not a batch +The one result worth keeping in view is **L-5 at `DocumentEngineBase.sol:264`**, which is not a batch loop but the linear scan in `_removeDocumentName`. Aderyn reached it from the "costly operation in a loop" heuristic; it happens to land on the same code as `IMPROVEMENT.md` item 4, which flags the O(n) removal against the multi-subject draft's expectation of "index tracking to support O(1) removals". @@ -31,12 +31,12 @@ CMTAT upgrade. | ID | Detector | Sev | Instances | Disposition | Reason (verified against the cited lines) | | --- | --- | --- | --- | --- | --- | -| L-1 | Centralization Risk | Low | 2 | **By design** | `DocumentEngine.sol:24`, `DocumentEngineOwnable.sol:24`. The whole premise of the contract is that a trusted operator manages documents for a fleet of subjects; `DOCUMENT_MANAGER_ROLE` (and `owner`) are that operator. Documented in the README and analysed in `IMPROVEMENT.md` item 1, which concludes the global role is the correct model for the single-issuer fleet this engine targets. Aderyn cannot express that distinction. | +| L-1 | Centralization Risk | Low | 2 | **By design** | `DocumentEngine.sol:26`, `DocumentEngineOwnable.sol:25`. The whole premise of the contract is that a trusted operator manages documents for a fleet of subjects; `DOCUMENT_MANAGER_ROLE` (and `owner`) are that operator. Documented in the README and analysed in `IMPROVEMENT.md` item 1, which concludes the global role is the correct model for the single-issuer fleet this engine targets. Aderyn cannot express that distinction. | | L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** | Every file uses a caret pragma, intentionally, so the sources stay consumable as a library by projects on a different `0.8.x`; the compiler actually used for the deployed bytecode is pinned to `0.8.34` in `foundry.toml`, and `foundry.lock` pins every dependency. Verified: no file uses a construct that behaves differently across the allowed range. The floor is now **`^0.8.24`**, raised from `^0.8.20` after the previous run: `^0.8.20` over-promised, because `AccessControlEnumerable.sol` and `EnumerableSet.sol` were already `^0.8.24` and CMTAT `v3.3.0-rc3` moved `draft-IERC1643.sol` there too — no compiler in `0.8.20`–`0.8.23` could build the tree. `0.8.24` is the true `src/` floor; the full project including the CMTAT-importing tests needs `0.8.27`, because `require(cond, CustomError())` is legacy-pipeline-only from that version on. | | L-3 | PUSH0 Opcode | Low | 9 | **Environment** | Consequence of the caret pragma plus `evm_version = prague`: the compiler emits `PUSH0`, which is unavailable on chains that have not adopted Shanghai. Not a source defect. A deployer targeting such a chain must lower `evm_version` in `foundry.toml` — but CMTAT v3 itself requires `prague`, so that configuration is out of scope for this engine. | -| L-4 | Loop Contains `require`/`revert` | Low | 4 | **By design** | `DocumentEngineBase.sol:124, 142, 156, 170` — the four batch loops. The reverts are raised inside `_setDocument` / `_removeDocument` (`ERC1643InvalidName`, `MultiDocumentInvalidSubject`, `ERC1643MissingDocument`). Batch operations are deliberately **all-or-nothing**: a batch containing one bad entry must not half-apply, since partial application would leave the operator unable to tell which documents were written without re-reading every entry. Skipping bad entries instead would silently drop them. | -| L-5 | Costly operations inside loop | Low | 5 | **By design** ×4, **known item** ×1 | Four instances (`:124, 142, 156, 170`) are storage writes in the batch loops — unavoidable, and the reason the batch functions exist is to amortise the 21 000-gas transaction overhead across those writes. The fifth (`:238`) is `_removeDocumentName`'s linear scan with swap-and-pop; see the triage note above and `IMPROVEMENT.md` item 4. | -| L-6 | Unchecked Return | Low | 1 | **False positive** | `DocumentEngine.sol:35`, `_grantRole(DEFAULT_ADMIN_ROLE, admin);`. OpenZeppelin's `_grantRole` returns `false` only when the account already holds the role. This call is in the constructor of a freshly deployed contract, where no role has been granted yet, so it always returns `true`; `admin == address(0)` is already rejected on the preceding lines. There is no state to check and no recovery path to take. | +| L-4 | Loop Contains `require`/`revert` | Low | 4 | **By design** | `DocumentEngineBase.sol:118, 141, 158, 175` — the four batch loops. The reverts are raised inside `_setDocument` / `_removeDocument` (`ERC1643InvalidName`, `MultiDocumentInvalidSubject`, `ERC1643MissingDocument`). Batch operations are deliberately **all-or-nothing**: a batch containing one bad entry must not half-apply, since partial application would leave the operator unable to tell which documents were written without re-reading every entry. Skipping bad entries instead would silently drop them. | +| L-5 | Costly operations inside loop | Low | 5 | **By design** ×4, **known item** ×1 | Four instances (`:118, 141, 158, 175`) are storage writes in the batch loops — unavoidable, and the reason the batch functions exist is to amortise the 21 000-gas transaction overhead across those writes. The fifth (`:264`) is `_removeDocumentName`'s linear scan with swap-and-pop; see the triage note above and `IMPROVEMENT.md` item 4. | +| L-6 | Unchecked Return | Low | 1 | **False positive** | `DocumentEngine.sol:43`, `_grantRole(DEFAULT_ADMIN_ROLE, admin);`. OpenZeppelin's `_grantRole` returns `false` only when the account already holds the role. This call is in the constructor of a freshly deployed contract, where no role has been granted yet, so it always returns `true`; `admin == address(0)` is already rejected on the preceding lines. There is no state to check and no recovery path to take. | ## Delta @@ -55,9 +55,15 @@ Re-run after the `v0.4.0` dependency bump — CMTAT `v3.3.0-rc2` → `v3.3.0-rc3 `v5.6.1` → `v5.7.0`, and the source pragma `^0.8.20` → `^0.8.24`. **Nothing moved.** The same six detectors fire with the same instance counts (2 / 9 / 9 / 4 / 5 / 1), -on the same lines, and nSLOC is unchanged at 307 across the same 9 files. The only textual difference +on the same code, and nSLOC is unchanged at 307 across the same 9 files. The only textual difference in the raw report is the pragma quoted under L-2 and L-3, which now reads `^0.8.24`. +Both tools were then re-run a second time after the behaviour-preserving **style pass** (functions +reordered by visibility group, global imports replaced by named ones, NatSpec completed). Findings +were again identical in kind and count; only the cited line numbers shifted, and the citations in +this file and in the Slither triage were remapped to match. That the finding set survived a +wholesale reordering unchanged is itself a useful check that the reordering changed no behaviour. + Two null results worth recording, because they are easy to misread as "not analysed": - **The pragma bump did not clear L-2 or L-3.** Aderyn flags the *caret*, not the floor, so raising diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md index 489fdef..b340516 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md @@ -50,23 +50,23 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Key | Value | | --- | --- | | .sol Files | 9 | -| Total nSLOC | 307 | +| Total nSLOC | 310 | ## Files Details | Filepath | nSLOC | | --- | --- | -| src/DocumentEngine.sol | 52 | +| src/DocumentEngine.sol | 54 | | src/DocumentEngineBase.sol | 149 | | src/DocumentEngineInvariant.sol | 5 | -| src/DocumentEngineOwnable.sol | 28 | +| src/DocumentEngineOwnable.sol | 29 | | src/interfaces/IERC1643MultiDocument.sol | 13 | | src/interfaces/IERC8303.sol | 4 | | src/interfaces/ITokenBinding.sol | 8 | | src/modules/TokenBindingModule.sol | 36 | | src/modules/VersionModule.sol | 12 | -| **Total** | **307** | +| **Total** | **310** | ## Issue Summary @@ -86,13 +86,13 @@ Contracts have owners with privileged rights to perform admin tasks and need to

2 Found Instances -- Found in src/DocumentEngine.sol [Line: 24](../../../../../src/DocumentEngine.sol#L24) +- Found in src/DocumentEngine.sol [Line: 26](../../../../../src/DocumentEngine.sol#L26) ```solidity contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnumerable, ERC2771Context { ``` -- Found in src/DocumentEngineOwnable.sol [Line: 24](../../../../../src/DocumentEngineOwnable.sol#L24) +- Found in src/DocumentEngineOwnable.sol [Line: 25](../../../../../src/DocumentEngineOwnable.sol#L25) ```solidity contract DocumentEngineOwnable is TokenBindingModule, VersionModule, Ownable2Step, ERC2771Context { @@ -239,25 +239,25 @@ Avoid `require` / `revert` statements in a loop because a single bad item can ca
4 Found Instances -- Found in src/DocumentEngineBase.sol [Line: 124](../../../../../src/DocumentEngineBase.sol#L124) +- Found in src/DocumentEngineBase.sol [Line: 118](../../../../../src/DocumentEngineBase.sol#L118) ```solidity for (uint256 i = 0; i < length; ++i) { ``` -- Found in src/DocumentEngineBase.sol [Line: 142](../../../../../src/DocumentEngineBase.sol#L142) +- Found in src/DocumentEngineBase.sol [Line: 141](../../../../../src/DocumentEngineBase.sol#L141) ```solidity for (uint256 i = 0; i < length; ++i) { ``` -- Found in src/DocumentEngineBase.sol [Line: 156](../../../../../src/DocumentEngineBase.sol#L156) +- Found in src/DocumentEngineBase.sol [Line: 158](../../../../../src/DocumentEngineBase.sol#L158) ```solidity for (uint256 i = 0; i < length; ++i) { ``` -- Found in src/DocumentEngineBase.sol [Line: 170](../../../../../src/DocumentEngineBase.sol#L170) +- Found in src/DocumentEngineBase.sol [Line: 175](../../../../../src/DocumentEngineBase.sol#L175) ```solidity for (uint256 i = 0; i < length; ++i) { @@ -274,31 +274,31 @@ Invoking `SSTORE` operations in loops may waste gas. Use a local variable to hol
5 Found Instances -- Found in src/DocumentEngineBase.sol [Line: 124](../../../../../src/DocumentEngineBase.sol#L124) +- Found in src/DocumentEngineBase.sol [Line: 118](../../../../../src/DocumentEngineBase.sol#L118) ```solidity for (uint256 i = 0; i < length; ++i) { ``` -- Found in src/DocumentEngineBase.sol [Line: 142](../../../../../src/DocumentEngineBase.sol#L142) +- Found in src/DocumentEngineBase.sol [Line: 141](../../../../../src/DocumentEngineBase.sol#L141) ```solidity for (uint256 i = 0; i < length; ++i) { ``` -- Found in src/DocumentEngineBase.sol [Line: 156](../../../../../src/DocumentEngineBase.sol#L156) +- Found in src/DocumentEngineBase.sol [Line: 158](../../../../../src/DocumentEngineBase.sol#L158) ```solidity for (uint256 i = 0; i < length; ++i) { ``` -- Found in src/DocumentEngineBase.sol [Line: 170](../../../../../src/DocumentEngineBase.sol#L170) +- Found in src/DocumentEngineBase.sol [Line: 175](../../../../../src/DocumentEngineBase.sol#L175) ```solidity for (uint256 i = 0; i < length; ++i) { ``` -- Found in src/DocumentEngineBase.sol [Line: 238](../../../../../src/DocumentEngineBase.sol#L238) +- Found in src/DocumentEngineBase.sol [Line: 264](../../../../../src/DocumentEngineBase.sol#L264) ```solidity for (uint256 i = 0; i < length; ++i) { @@ -315,7 +315,7 @@ Function returns a value but it is ignored. Consider checking the return value.
1 Found Instances -- Found in src/DocumentEngine.sol [Line: 35](../../../../../src/DocumentEngine.sol#L35) +- Found in src/DocumentEngine.sol [Line: 43](../../../../../src/DocumentEngine.sol#L43) ```solidity _grantRole(DEFAULT_ADMIN_ROLE, admin); diff --git a/doc/audits/tools/v0.4.0/slither/slither-report-feedback.md b/doc/audits/tools/v0.4.0/slither/slither-report-feedback.md index 7c1d254..a79f44f 100644 --- a/doc/audits/tools/v0.4.0/slither/slither-report-feedback.md +++ b/doc/audits/tools/v0.4.0/slither/slither-report-feedback.md @@ -16,7 +16,7 @@ All four results reduce to two underlying pieces of code, and both are correct as written: - **Two detectors (`incorrect-equality`, `timestamp`) fire on the same line** — - `DocumentEngineBase.sol:250`, `doc.lastModified == 0`. Both misread an *existence sentinel* as a + `DocumentEngineBase.sol:282`, `doc.lastModified == 0`. Both misread an *existence sentinel* as a *time comparison*. See below; neither detector has a notion of "zero means absent". - **`dead-code` ×2** flags the `_msgData()` overrides. These are not dead — Solidity **requires** them. Verified by deleting one and compiling: `Error (6480): Derived contract must override @@ -33,10 +33,10 @@ through, it is the default of an unwritten struct. | ID | Detector | Sev | Conf | Instances | Disposition | Reason (verified against the cited lines) | | --- | --- | --- | --- | --- | --- | --- | -| ID-0 | `incorrect-equality` | Medium | High | 1 | **False positive** | `DocumentEngineBase.sol:250`, inside `_removeDocument`: `if (doc.lastModified == 0) revert ERC1643MissingDocument();`. `lastModified` is only ever assigned `block.timestamp` (`:282`), which is non-zero on every live chain, so a *stored* document can never read back as `0`. The comparison is therefore a total existence test — the same idiom `_setDocument` uses at `:276` to detect a new name. The ERC-1643 spec requires the revert-on-missing behaviour this line implements. Making it `<= 0` or a range check, as the detector suggests, would change nothing and read worse. Covered by `testCannotRemoveMissingDocument` (`test/DocumentEngine.t.sol:450`). | +| ID-0 | `incorrect-equality` | Medium | High | 1 | **False positive** | `DocumentEngineBase.sol:282`, inside `_removeDocument`: `if (doc.lastModified == 0) revert ERC1643MissingDocument();`. `lastModified` is only ever assigned `block.timestamp` (`:322`), which is non-zero on every live chain, so a *stored* document can never read back as `0`. The comparison is therefore a total existence test — the same idiom `_setDocument` uses at `:316` to detect a new name. The ERC-1643 spec requires the revert-on-missing behaviour this line implements. Making it `<= 0` or a range check, as the detector suggests, would change nothing and read worse. Covered by `testCannotRemoveMissingDocument` (`test/DocumentEngine.t.sol:450`). | | ID-1 | `timestamp` | Low | Medium | 1 | **False positive** | Same line as ID-0. The detector flags any use of a timestamp in a comparison, on the theory that a validator can nudge `block.timestamp` by a few seconds and flip a branch. There is no ordering comparison here — no `<`, `>`, or deadline — only equality against the `0` sentinel. A validator cannot set `block.timestamp` to `0`, so no achievable manipulation changes which branch is taken. The stored value is metadata surfaced by `getDocument`; nothing in the engine makes a decision based on how recent it is. | -| ID-2 | `dead-code` | Info | Medium | 1 | **False positive — required override** | `DocumentEngine.sol:118-120`, `_msgData()`. `DocumentEngine` inherits `Context` through two paths (`AccessControlEnumerable` → `AccessControl` → `Context`, and `ERC2771Context` → `Context`), and `ERC2771Context` overrides `_msgData()`. Solidity therefore demands an explicit `override(ERC2771Context, Context)` in the derived contract. **Verified empirically:** removing the function fails to compile with `Error (6480): Derived contract must override function "_msgData"`. Slither reports it "never used" because nothing in this project calls `_msgData()` directly — but it is what makes ERC-2771 calldata handling correct for any inherited code that does. | -| ID-3 | `dead-code` | Info | Medium | 1 | **False positive — required override** | `DocumentEngineOwnable.sol:68-70`. Identical to ID-2, via `Ownable2Step` → `Ownable` → `Context` and `ERC2771Context` → `Context`. | +| ID-2 | `dead-code` | Info | Medium | 1 | **False positive — required override** | `DocumentEngine.sol:146-148`, `_msgData()`. `DocumentEngine` inherits `Context` through two paths (`AccessControlEnumerable` → `AccessControl` → `Context`, and `ERC2771Context` → `Context`), and `ERC2771Context` overrides `_msgData()`. Solidity therefore demands an explicit `override(ERC2771Context, Context)` in the derived contract. **Verified empirically:** removing the function fails to compile with `Error (6480): Derived contract must override function "_msgData"`. Slither reports it "never used" because nothing in this project calls `_msgData()` directly — but it is what makes ERC-2771 calldata handling correct for any inherited code that does. | +| ID-3 | `dead-code` | Info | Medium | 1 | **False positive — required override** | `DocumentEngineOwnable.sol:82-84`. Identical to ID-2, via `Ownable2Step` → `Ownable` → `Context` and `ERC2771Context` → `Context`. | ## What Slither did *not* flag diff --git a/doc/audits/tools/v0.4.0/slither/slither-report.md b/doc/audits/tools/v0.4.0/slither/slither-report.md index 6555ac8..706c40a 100644 --- a/doc/audits/tools/v0.4.0/slither/slither-report.md +++ b/doc/audits/tools/v0.4.0/slither/slither-report.md @@ -14,8 +14,8 @@ > | `timestamp` | Low | Medium | 1 | **False positive** — same line; equality against `0`, no miner-influenceable ordering | > | `dead-code` | Informational | Medium | 2 | **False positive** — `_msgData()` is a *mandatory* override; removing it fails to compile (verified) | > -> **Scope check:** `grep -c 'lib/\|node_modules/'` over the tool output below returns **0** — no -> dependency code is in scope. This is a Foundry project, so the dependency filter entry is `lib`; note this +> **Scope check:** `grep -c 'lib/\|node_modules/'` over this report returns **0** — no dependency +> code is in scope. This is a Foundry project, so the dependency filter entry is `lib`; note this > differs from the command previously documented in the README, which listed individual submodule > names and would have left `lib/RuleEngine` unfiltered. > @@ -33,35 +33,35 @@ Summary Impact: Medium Confidence: High - [ ] ID-0 -[DocumentEngineBase._removeDocument(address,bytes32)](src/DocumentEngineBase.sol#L247-L262) uses a dangerous strict equality: - - [doc.lastModified == 0](src/DocumentEngineBase.sol#L250) +[DocumentEngineBase._removeDocument(address,bytes32)](src/DocumentEngineBase.sol#L279-L294) uses a dangerous strict equality: + - [doc.lastModified == 0](src/DocumentEngineBase.sol#L282) -src/DocumentEngineBase.sol#L247-L262 +src/DocumentEngineBase.sol#L279-L294 ## timestamp Impact: Low Confidence: Medium - [ ] ID-1 -[DocumentEngineBase._removeDocument(address,bytes32)](src/DocumentEngineBase.sol#L247-L262) uses timestamp for comparisons +[DocumentEngineBase._removeDocument(address,bytes32)](src/DocumentEngineBase.sol#L279-L294) uses timestamp for comparisons Dangerous comparisons: - - [doc.lastModified == 0](src/DocumentEngineBase.sol#L250) + - [doc.lastModified == 0](src/DocumentEngineBase.sol#L282) -src/DocumentEngineBase.sol#L247-L262 +src/DocumentEngineBase.sol#L279-L294 ## dead-code Impact: Informational Confidence: Medium - [ ] ID-2 -[DocumentEngine._msgData()](src/DocumentEngine.sol#L118-L120) is never used and should be removed +[DocumentEngine._msgData()](src/DocumentEngine.sol#L146-L148) is never used and should be removed -src/DocumentEngine.sol#L118-L120 +src/DocumentEngine.sol#L146-L148 - [ ] ID-3 -[DocumentEngineOwnable._msgData()](src/DocumentEngineOwnable.sol#L68-L70) is never used and should be removed +[DocumentEngineOwnable._msgData()](src/DocumentEngineOwnable.sol#L82-L84) is never used and should be removed -src/DocumentEngineOwnable.sol#L68-L70 +src/DocumentEngineOwnable.sol#L82-L84 diff --git a/doc/surya/surya_graph/surya_graph_DocumentEngine.sol.png b/doc/surya/surya_graph/surya_graph_DocumentEngine.sol.png index 8ea7d1c07d65bb7c986aead71afe6221ba6fdee8..ee636b27586be58f88aac08bc3bcae39aff026f6 100644 GIT binary patch delta 98868 zcmZ5|cRZH;`~F=-5=qG3A)BnoCL`Ho6SA`RzNE6ZgzUYtix9GtmAyjtCM28R>G`~V z|9r1kPkFj^-`DkipYQWLj^jK|pOP^n!!XNMA0so*pXwuM-<1AgG(UdNEhS~IjxKdg zyj7>QSfl8w?aN{!-JIbnt91*FPogq5>)!-*#Ea51ui8qZmvg`93gEoDVD>A_Yev*I zX}5wL{i>sg5>?v5(o%9M|G%ZA2Pw%py}kJ6<^@hvHKcfd6;+wzEG;b~DTO^x4;{?R za#afu9!^gCqxjdaB{^&w@UNpkeE6_0O;qX0lP5|_GI(z~O>wD2eoakHwY0P}HC>5F z_IlQ|QC!ND6VKofn_8ZhQ#oMBZ7_%8S4+6<5&XKlNzu$an`gi^Ojw#OhbuBMzN@>( z=tXwrKqyI@z_*k8#}A(*2g(Q3hE8pox~|C);vwk!T326D)6mQ|y~ezM|2}?j>#twG z;M0bOT~>Z(EG;cfL@2g!-eh5A^*%p6ie*&7zjNoU1Wj6>o0Ah6zio1LHGgEspsQaL zL(|5lzNTccN3+jv!rmd)f!wuX|F`7<@M*I80kQ!CPf})opN$TEE!ZVF(Wc8$F)R?3 ze}w#ay1{pYP}jN?{q{`%{-oS7ua6Z>6u}x$ zyh3{v7fV`t2MKj6@;7*%$+>g;DC{NaLF$5VaW`rc`yKrYQlU-CgK4%DU1=nYwjoB; zfM+_dv*XUr8*OfMmpp%e|L@{f; z@YlJyxr2F1X%C$k^pNa$|8d;x_oWciO3$~A;_7Butw$W^{^xHyesy@t*ZT$d ztv0W^=BHH6udOp%#>F4SbFAt!=f&5_>gSFsr44ZA9M!hsp<7Dn=pYlbErC=b-YW|W zpS+KDcjh}{W&bd)lPNP1#B&&mxNc~@c(J(98T+hQ=Y^$Z!Df2;=(Wn~X&J>BnY}C} z;#8)r_;AAT6!zq;d%OPnF2Xl(|1c&q#vjJdInRG#-fFIW>$JY?sG(=maZP;NzqYm( zqG@Gi1wQohX$A)owaMH2#>{bR6Q5r;o*%#T@bFMp{yC8IWcWlnQ< z+2?)N!XeE^C_C`KO(Cgm@e0GhVt(I0oWMpI&Lg#OA#muh0U4MFN%B*fZ z-v#cM{iFW%c=L5>JZ{J7-`;P;(Q*GM6^X(_Dq`4I-9@h_$xuFAj5lPsckk_+H>~=# zpPp0~ttAbGe{fprK04j&J2}*wqQr%F6?<6k9XKB0_0?;ezo9Y1>UH^5#oKFfcsCrs zAO3uJ<`lt=bo3A=rDQ8kL|pgx3?CXA>Jz<4me1K5uk08f*PfjH+BG*nf4MMDPJ8uU z2mewCcRf@1d!~(Ei@Vje)IFGVb-h+U=ia|VC6D+O=x(=ZIPh!QhA{^jKmYia54&$Q zZO~N;Z%F7@BZ3_5E-ET2Mn^|;x~yB>VNeJP3(H27nL>%!e)RQ?x(Y;@K`@;d(QlOP z%qkU&?h|>Wu?Q?PyAHcbMPZC}ZbT4L=+bFbc3F=;4h>hWP@lXlt{myQJzc-QKB|=Z z$m8PtOi<8%%)*V6pI_8tH#;R|YOXEx!Gi~%DR8r|lr4WsGNNbge#9!y|6HvtSG!qz z0v8h*_-d-SyZk;)_dA=f@6m(1uLo6V@^Qw;$FYf66ZmWA1BB@eH_S$<{ATjuwv?? zhj!DCl$4Zi-HQDE`}fPylEuZv6afc`d6yJK$W4(RfB%%5w@YKgvtq^cUcF=SzRo?l zf=zqs{Ep~OQ%JJpg3c>-(X|D3@@YQ*5}h7M2IB7j{i|1I)U?1bl5kf31 zRyHTAgXy5^q;Z)%R#zX&lnyP?t8qN}xA`=QPuT09^-_1@{{H@lVtwYgR6&=*z;`w9 z2yl)D22^|62UE{2F47FolRJOhxqEb`O5jfjvzQpgi>6ffo@MW>MqACa^^Q*H`!@`mHJB6u=h9v%z60A#!2q~znrkAd$< z@bQ~Xx#L;2E1XvclKAbIeh+sehDJfx-;@R~Ji;Jgf zU0ymn^Pr6WuJrfz_QElMm!_wuw!)&45`)iH!*D)Dk5{s~;yF%EPTVBb^wK%)gRH-E zJT5&)dT_++DRAxQ`k*LHp4d>W3%GXr-Rt-4$FIn4OYHCO7Zem!%TwH}8pFrOhe(`X zTKYE|NR^wDa~o54=H9^IAPWnNq@-j=IN9>rTCM$T^Tvkt&)KQco+MT5fB*iOa+65+ zFTHM?{Oa&|wLP2+?vb3!SV~MxOiwS_tQ1jDR#vvL*@Wld(PbSzcz(! z82zYyhn(CSBEGDwEFmG`Mc4ZR^+LObBL-U9Oo&w2fy>+%9{%~$r_+DPq@4ycA#?6z${ zMn>j>g1~Tfa8MBq4-Y>(UN7ah9{clWR+59GRKL!(t-Za9DYnO~`tIg?HeDTcMxxLw zC{yn7$;p$S8ItBmZ*s=qYEx5F*L$}0+FSvA>Ykz`w?H=;1tK@;BfKHB=P0#kb>eTT zWA~HT_#z$1DwF%XaP+JZnwyqh z+#V~Rhevu6&wjSoCu-jFVc1i&4h}$}I+jW~PypWGNFh7JMb99+LIm4Q)$Z5cJ;te_v6YYEj=g5SB$!3y(;BcW38)e*R~bFC?b0Y~N0QUMtu~`ISsw15VK0-@j8m z<6GyxGY6l~@btrwF$V#lM_9XGkhcIkA0Hp1P%gVN z8)jMR2s6pefaquyS=sMjJ@zhB337hQhdnwth=%&RJ!9ze=5ZFUsJOo4bYJ+$}EDiFYu$~&%&QbCEM z_!SEw({U`G&%}tnGks6EVitueAiIi~a3@~(XCk;U7hqyxk#Dg4abJJ#^k@&|_vzE8 zO7p(CAMQ_&s&{)qoSguLx~E=rUe@ucsc0#`KucmL^wIij>N-D zE4r5V;861FGW?fJJ_`_IqJFvvTfjrp(9pm^larGJ2n5yD3qsKx=}WbF`SMA!k*zH* zD!-(p-62`Z>LgcNMdb=As?&6huD!iofuS-t7X$UPX`>9vvG-2f-Hr%~(4e5}xVQ$D zW>42+CyPGu&fVQ)S^nlqZj&_poY)mI$lh{aqx%jmC+DA`0=4(=-MYf(n3Rfty^sixrdFF zmG$An8jnb+4*rs*ATJ{c35j^c?I`&idy{bk95XxpE=2wL4-JPw?N!Na$=VVk{NRPH z%gf-|Gq=@&93_#X*6-gfAQkxf`l4anB<3^{gYA}=4=H*%Sto>d=k5(`>?Epe)dC_S zB8pKVd${VXvyvQoDnjb#f*-S5QkMeY|Q3E|M;I*=)KeHa0#{A!=OskJ}1>Pl-MMO>BCT zFi+&Zm#UWX*zp28x6wDh^z?MEgDnp)uf3yF_z3UaPDY#5`_ZyQY&r`Y8{WVKxD!wR zt`7DNJzwQty(^E(u|LW$q@)`JjraE&yoZslZo)n>jSaaH;3tgIWAeIj02n=*pi*Y?_Yr(RwQ^N&`f%Ntk1{(Mu|+WQd5zxQL2 z$IZ15@UO2?nkFkCSLvU%y6Co5op5YX-50?h!~5QRK0{v!hzD@g+{J;dJ)HY@@7`rd z1{OZM|3SdP7_RlYH__3zQNP!Qi>~|sg2WB!LABshdirl5w~#FD$?bgAU^xIbN@U=P zNA2!9W{v^PxJ4!Mh?&_6PBv^Wb6jCP8FKgVAOa*6<~Vtho9*y^i;l1XUrI|$0|Pr^ z?mY=gs>ubp=lh}~f*?5Wv0NT}>9cejGj3R<+@4OmhJ0XF_)IA2>w8|HmKPHfWA*Z- zHtYNF@E*u{Nf*DuLPLc>nNd-Re)URROzhi_IF{fxUR71qpgR(hlJj$OLr53H+kgOW z;xH)3knU9rh8{XCu^KicOVRT3@s(&-<^oC{%zbL!n@rQYymtiH0rne(N@uFI9LzmA z*w)q2@jY6Mhn=qTI$-Bo^lvjx5%CcOtXEKA2`6QJwA9Vn86CyL;RL*9Q@YNm$$wmyJBWQ~(RlRy&-OE)A9=WU$yp>p8`!+Bz01$7)_#Pi{kp~YdN8Ew| zK|viz`tSjsTHdMV-=tJ|rO8<_eQCv*(n_89rXPn{$nQws z5MMR}=8SmL@`@Y8*I+5GJ{G@Le-({bsm&=y5GVd76)VLCtBddA0Tuc_)78RnT1U07 z*Rip&0b*SO5B90h6#Myh9CUHB-ekE)ah;|wUc4~$*&nWPTAqI8Q!7Zv!}Av)5%6^C z08&X|bP#p&Z|F$>@iSs?N6rcs7@cZrs#;$0ltvF6zFoY#`th%!!TGLWtUBI}n4a!l zqZg(X+z+%!7XLb;#6+bVX$EK%@fjHlVq?35i>zvdG0^v|prX(C98A4J!vwvsuH-$y zPT#-%GEjzJrsmHI_|CNk1868Is$LfIJw1eP14)s~xb?aX(sA`n6o70EvjyS$$5D-+ z%&zY@Hv)u}GPd6JbQ{=xrlfn0DUT~4Pcf7E216b#y8Q**jCSJH4}R#fBl-LGo%YYl z*JXEP1$+oS`K{!|{vc)?tq`$1@)cZ zE)}+C0##XUKrJt7Rk~`B8d9C%*wUIC3<{2?S3&X+g_+O|lD9OxG(|5pV|Um|l(J|G z91|W!)PH8}%r z14=GI{Gz+-!S1#ne~nRX&Is>k(({C;yziD%oXyTIAA}posV z8laT#5jNehurSw+G4XEpK%T9wsk#~86SkzDb0}@YCwLOVco40zz>P%{&ZF8D$?j2p z&Nl0`F#S{o!XaC1+)+}$9hM+g`;B=e^fU!R#bHf)CakCCd5lgmRQ6P+W!$oUJbK*T zH@Vx@79Ps-$?B?z|6DKn`0*w-Hb{}<-ALkt(GvY98)7#srR3x~Z;75>YBiAkOB;?u zy55Rzdh#UH?~5o2C`FJ^c(wX0FuCX{R~s1*wh|TTNeI7+$^P0`hiG1Gm~73zs(2Om zN&dDpI_h`PA=(QxXfgcb_VP-sr?Bd1ADf~w00A{P>D_m^uN`$DRCg1YrP3lw9MN~n zYs*%u8ucDbu z$LbUj8%7zc=7>$oWVL(sZsL(AvoJ?=Jai5i=Q_Xrj;9@x3~rmtl8HS7uUtUj(2gs9b+9`H7rCfsjacN<~IzFxYfU*FpJuBDsA5BHDV z-vi5Chj&f4X>^n&IOKlH1r3HjG0#cr>dE6(NMcT&*r}knS}2y%O~`oz?!z%AIYg~^ zainMOp;OI6tf<=AQe{Ps#-2K#csKsUScSHNE#m_y8QTooTipyaEBnKvqiKRJYasK| z$97hlAw3Y_7{BDscysddWTd6_YaFGOlzM|M9UbTWFlilV#1T9Iq_C?Aq?84sf<S|z&zD7pWDY!`L zz6ht6*C=eZ-P-imuDYbQAI2uucY{L%$66>H+};VgDxEwvq)xwazI2-MUC#TPm$Ecc zv{ox_a{b!X7nCG-QY8&6jMe0p)T#GMG=F+bp>8~cK>Dq-eqXXhDoufMS>lcG!^2+iuX3{$piZ+^wi)#mRFjQ6I0O%_8qzX?? zP3ccMh}+#Vv)le??71U3-9nEjs&X@GOWh`6!ui!|yB$0O{i<71n5y;yUuJQPP+pzx ziM{eli`fhgF+Wxp6~Z3sasPsj87aw`Ytp^1DMTs7*TSy83sbcZNo-b_5-_K$EEl)= zE*86Kdhc(HMg0n~ufnxr;~?kn>a#-!WHX~0O+L3WD4}!2z{kfYB65yEVT7;*ZF{~W z(#_2cFzZ8&%-KZWnS0s+hC1*4KVnix0h+^B| z*0lKCgG_@ehsL$nZXT%E+$r}G#}J|mk9*(rLyYM&;p?^p{Ay$!8{DgsPLo9>v0%ylW9Zc$p{ZT8e|D*CItrv=j7zD5_LgGv#$?y z(9n)~OCdp76VUmV-}QH?BI5;c@>w-CF<2zURqeS-2(9)F`ICn_WyhDR{z-n9#mb^j zi&k0?gYP1>!a@0s@35Yif>M)^K4jO?7p26fuuQR9&4=bYb&zn{9eX&X(L2 ztVxL}9o=0fON$S7_Yyq9-#te8HK@+$*xKByu6U=b^Mj2|<;jzpoYXZ6ZQ?sLbUA+q zR6Q+8vOc~+3{>Sg*fQeP`wLMh*Z{zsT^pB)JO2!xF|}~?Lg$_)vkHIjiwBi2Oi4%> z57hovO4|&_>e?*u*>T;cc^&6qaN+&YmX#z*)Z)WepoKb$Ka#BsqH8b=H}i|qsxgdZywXKwEoD+ zr%6n9Km3r{Hx)Q796|B<82V ztwXA|maiwqvi_YdoJ}9qP!MAkrEBmQdPJ;P=gji3)Wp>~^x}T5jMDvTmPi}Iq{DHX zd|re9xDz@N(w{>ixb=^#{nC5YmD=RB70NC2v!nVR? zYm30wwnj!jd!9PW=Tra)f_^fzBR+l%a|b%eN^s)}%3B2a{q0p%RTVBSE;~DWef_oD zo@$(4uMNcE0QDCJ@iAX{n%?`Suwb}#=?Z#DNKxLve9>Vov%Y>#2>|YAIbF?KdSP9y z3C>|9uU9!aG0@Eas6WD|R&lV}6Gkw`h;@Z^@1PM&h!1ge%5p!{#zo;pOFLHR$-fWj zG=BF+o(UgIoA#a@SwVRCyLJqDNwl1d=z%d=8|!qQ{F&ZnEm(BPoQ&Y+R#@~*PMQH; zC`34UE=P;&I0W?4T=iO00(o1!41&7D;ZCnjUkgqXT}2zavlR|WX!w=4;Gg6Cq(dV;CTmHc z`c{faI$IeI*?qYXWjqpX2F6ZaUEXVX_MEt6CJ(rBOXQy^K{Xv1R9VB*&PtV!EL|XD z!cib#u;(1nT`|%PY8q%-Ym=XtB%)1163^;px65n6C(+XOIn*56yv2qr-QY$`8PvTW zKYoBbyHucIV`HPH7As3Hfrs8w)~ha_qgn$G-37HAE=E z7oU1%p%@$ef(h%M&W196NQ}xp7H!3K#VD=%0YY5@@N*!c?9Bt%*2UPF>)<` z6B}J!oQNg#`?7(PZjijrB38?tyZan#h0b?JSySnf2NON;@rF#~a#UUJd{8ZTCrjVz zkf^{Af3epBq^r>U)*tsrnZXej)vvL!CGDWAjfY-ej$8G%OlA|d9#<9fo}5SXAE|e~ z1lT5QCHjv_eJP`nP)QI%>_7_yKY(8l?GKZM05m7q*yPMwyzL5yD;5cAgTrZ0qxAfM%e%(Lajq<k2RhY$*aI2CN^5ADVMiBBS4{@ZcS{IbqH4vai*2|EkaRqH>_Z ziN-CL+$_x3sOi=-zAw^ai+x2L@9);DaWZsqo!Hi=6ECWY-f7mR+ct%KAe4`(*ssDP zNH)&pe3{zU)T?>j{}>v2@P<$2V|K%u64Szwr2=;{W_|_h5Ba`wmz9q>Y1zrf7Qyc4 zcUyW(ozL<5@|`E1Jt>~Ds}TJ_C9KF}`=pKNℜpyw77qw6Ss8Pl~S~PF90M z5|~m9TS8~_4?|q}mVeAolgAy1?}OtWxwTK9y^C%n;Fr#%V}#fTyElQbG(5 zeV-k24YZ;?n!IUV)V7(P68gTx2K0UP(6 z;ZR@quLbe~+Z3!$Kg`u)A`=!Fh97xcvLS@i`^2JwO^S$xB_2s+VF+z%oRr8XTL#p9 zliXGeOI%qAy^M`#)Rykmmfw+_ZW~+Kj%tV%7IcEu+ z8Fci&Eyb*U>qou9_U&@x4RZZd@VKd4i9QSO?#goL453|yXjJZhFHSS2B{sT$Gj?k| z>lmQ&+WuC-SN+zaf7~i?Li;-#kE%YLsaFs5=-Cm{`#Ap8Z2p(4Q4dhmL;8Y{YS`zm z_OSUxg!F?T8=9eTr!^azpk7Uv`CruI>DBAp=S>@dGdR8nc7HEE9#{JA`hLKr@=xDF z`)|*RKszX9ZJ5xged#!FYSS@r@4tVlhxXiGZ&wYoZTtlVtlU*tIiA;<1gdc^1R8gwhow}4&wYMMY0{V~NVJ>M>nS$j;g`@B zNx@g`rfUZ=sK=T#Jx^NXc*0I+lx@ShTjiX?y4&T{!t!i&2#FMF`Wl<>koEUc$3NN&cIqn8ZELoDAAXut3o&wo%b zBr?jzt>@U*M1wOE6c|zz&3OjJA1U|28Ct>*iO^dB3OIo zxDYAYM1_D%rs!%HJ(1`yU?QWP0)T)*aVK^!o)2!xX7i?>{gU;GO7G2?7~H{*PE%NTD7L2X!MOlA9HP1wE41K+ zHnYm$XWFbpksWlY1FNOB$8UMC&FqvXzbAyfBH}hZx~YBT4qJBDlhs{r{D7+{dwdP? z2WmgV;&|@d>k$*@)l|iK$76zA8y15I&95bvV`*HlFI1IP8BvRq_)xJlgE_43LHN`_ z`kCh0ok!_R#6(P4qCZwR%>T-eoW1CN#T*jBcQt?~%in-&%GOi%>366@aAZ$N?}vMI ze|_}Wfbszc7(n~FWxiiWeG{_;@cYt7gd~c~FPtFqmsX8Kl(q6Q8UhiCgM$MC+;xBI z>jO(@{f&6&U{fi^woDbqyY)qnJ*mfxJF%xSaKuK|SujNa_9BU2UR(-ShKGmGuGsoK zPm9$yT9}`gx1iFzTuZ^UyBHff<&P*MyB610R_tr^=B~gB;qIPGKU&&5f?noH={F$^ zGrOOnOvKGGh-n4=Dyt=i+6T#J>L~ISIp=o|Q7^{QonKyS(Nla7KiCuJtoHS8?`{_J zy2Y-1?bk3Gj@+&rBoVRI`qzy1CY>+$(Q4nR z=!xvbDp^7LvjwhQ+I>1IM=EAHn`(6xhr*ET4?lUHOD-Y-z9ChNJhJpLpkPCfHsC@D zn!#@+_6Lc^iDNsh&j}SDQ8!E^A>(%*CJKrUo@)52N!Xk)ZV(gIi)^?%&HXz3(1a9uFyaMadsgp0Lz5`@j| zwRSA#K2duSD@L6UjTMmoi;Hb6EG*8iDP$r2LEAcWx>ap=%TiNQ1x44t#NJ+`Qzp>h z|H7wq7EFZ($)&tJ+WqI(*!WJ<-6L;k0KkJmgP{c>AtuJdJaLR<>z+O)zY$=cL}?!_ zg}eKPoufteCYE`^8)1bXl<;6GswaQ>L`)T$-)C3e>l>R}X5E<~xT&IgXq<|PH1COh zp@f7{h4yA5uB$ucrO-j>500hbzCl-3;ePA7agGFz2_Nk%C@hrmFC*dfnareZKK`7< z!bXnZCA`cWDJ7*yVqpw%QkiUrwRaJRXUfl><(;$jd#eV)!bf)l1zTNTPfAQoJn#DX zGb-WRP6-%g4aH-htpsiDk|K5vt@lwKImsE<&EF*m$v2aP#2#<;#Yg-mR(szySL8C6^FvUtIl zk8}5R*PetAfj_m>YiDU`={-ZC%YL^OtLC0}FVHDa9=?4On^dWWOubEtue^%zw33XZ z8p{pjSl`V!|NB@UAzaiyVHgy|XCJlv+IIK6O`5W6mFRN-Hre4ICwDSN>5J>vcBobw z%WP-mo8$rC+=tu=E>NJW@VmCQHn9Cms-K>fWwOHoKK{8^H8n?d!mXJ8wTd8EjW_P@ z?P1@z0lk=@06|J5(i~RpR;in(K`q4Yj<}y z=n3E_KRxxH>n#9_16*J?IfYLw%0d$oq(Sn#hDS#5FBl7NA0 zi62uU?k3dN(|yUr%+)v{8Tg8Ew#wVywczYzBQsR39Ji10IS3Uy%qXUNV#ApRilXK&OWu2VBq=|Mg2RN40Z)XJd0SIPI{NA3;w8>u)w5 z`W2KIguJ-;tK$O=U*84=29t6vVEB^fVEZ1o*#T$pD3pNiA8spr(^6lyG|oigxiG<1 z-4I%cPpUyRTzV$?!C+x?J@xj! zm!-dnk#$r&^Wz6{!_`khxinq10Ji19d0c#ao@xQy=cV?vu)yQE@EtnZZmU);&>Wlk zOIq?FZd+aD3n9@Awcah8K(s3;l-dUqgMYkt*zgiQmT=v`5WfFvg$}e)r;Q~k0_iZk z$e*_KOd;oI2Q#3g!Ug#Vh4S+9a;b&>sggC43v{P6Ak)ItwMsCNg`M4ulNba*r~fW8 zZquN4A+6{CMs%a?bF|yo&uaHvF!l<2L~P1eg|hYO)4B1a64a@r+_Sn^-I}%OrtDHT zAF)Hbw++7q;Ya5{NW<{pE`>XKq zL0Grv!{0&4(?0Vw)TnQUr;QEk2UDAOtLvPED`6oI|GQ#h@FgW%RI(mlHur04ARD{r z-UQmlgutPZTK95v3%zq!_anA{V7y8I;oojFQZ1f)9^-KRUm<%#AY2g5ZaBXO4 z2r8>U5KtXnFk*n6Au7s#eWbXy7O4eegJO!{K5Wpksy)b4xB7tTIOnjzqcy>hLI$0MhzWmWPE@hJ9A$Z+W9fDZSA2jI9{+1|eNRJ*wy zh@KzL!$iSl^WVW?Rlh)2TYFqd^lSk%YZaB9+-)0c>%aT^XPec?3=gG7$35_L1_lOt zd3eBKw92lt*H0!b46ysYTFu>4z`}iF-iXw9V?Yx zXvbf@dKH)rbQS)m+^@eGMLI)&97Ul+Od(s-^o`NX9CpVvOb-5Y#JU{h;Qe3R06J*7sZv>+pcMnK>YK4|qKBs_d^ z#~r2t^ucbo+~wxt;`8UXxP=86yTB;80k@dc=n3sy7~B9CH8j-A=7)-pWPVC`jY50m z(qzAh2>)-r?z6|&S@dT@`_%_*iIAr*-Sn_r(-z(n>#xU?MwH_|=f-(iJ$)ZsQ!){8 z&C|rO_C?Oov*Z0}R=I#GlU0@#fvM`wgMbKz%Ie+SU1%i#0Ve|ZO@!RGk^^Ow40D%Z zCofHj4)g6SUZD{Fbv*dYNSvV6P(JjVLG+K7l_KBS-Tm;;$qXFXVD%a{=i}jdgq%O( z;rX=ho_=Z7_n;blSz`7vI`rr8XrKZ^scyAmeLO};CJaW9Bq~f-nn&iokLw>E#<_9B z(bW|LwJ}yscmIBY$L4DcoQkqC_eC>GHWB|N!7sgRBKK|BB$26Ms<}ykG*_-%0kb)n zhjDJ*GAuW~R$ltrtZC!v%dkR3L&e!DEZ$NGZ-tF_rJiHytGNzV(oAinU96p;P<@DI zPV@kHnk6M9R-BJvuW}Vr3X3Tm4XA)n`NL?2a>39Ha#6pJb?H3@H{#<6@`o^0vH#4` z(sC66FNn9d_m8eFuoN#YFY`l`g9-c!syF%J{PObh(oz+;yyr{_q)m*Cp^*REp1Fl` zhb}Vs{-Cr&KjHkK@gkf;Xr!ZK8D>@1NFgf0I##MzlPMy>ngP>#St5>S$1YSQ4f)GZzH30aFL#DHzLp!Ct^Wm=j5<~Jsr+vwcU(CmGwBdxW&mYpr3bX z&j(IOLh{ghtPIBG1cijQCqA!3e-bQ%TwGl6SJ0L;g<6OD4O;~V!}}Qdd%u5buZMpI zM(>73N2!Edt()Iqm6w-;V;7tt#h%U+;At-|E{1WnkGS(t&%u$^=(7G^fq^cfwY|MJ zfh$Y8Q)*U<>e77x(-7ku2vN-=hq+cTq(jI60<5x~n#i1A-DH=*NW+14>;w)@(L%p z;8vBkbW#&N0|O*h21rccV1dRKJS_|qyc7|$hk*d_J;P)ISimi#$8JxsWYkCgjvK8p z+0-}yHzVTd>8Z-}u2{DkOy|O2)`JeHx5xz`dlZU=r3hvyV0r`w8LH~nXPTBLAArT3 zLe%%`r%!+X%(i@BBQdj^$QX3S#9Y9rzx9dY`p|vj5|$st-%1iLch<+^BEh-qqLBgYxyO#)<1D8DvwKhDlIZ!ld#>U7pB$D5D?m?nCxhjVHN% zTRcn}8yDBr*?GaS2HIA%(ctY?^yz(fUkx0~!3>$n$>h4aldFSB-V^#5u#BRilLICTvDE(zU7m{!|QLGL73<@e1mHM#ti~4?(Xq%kMB`5u!AsE0444+ zPU7MS)B0{ueVgB5P)dcSCA6Y~4`4*c2Rv{DH*$JH2X~eyO|*4& zOSLPP07x)}Se0od)LRBdAS-;$WZ}^;inm}BW&cp^ecoU~x}jI^mt*_J-RR7F(nYNDe2JP{y!l7^ z)jJrvF^^UD2VBtQXs33_7lNx=RwTWXyuq#}QCJoW)2<7g#9;^&(wFpww)OxQ!3KsO z2^c33m%?+Yo5Yx%5k<{-dF35_sTY5ZQ9yO4JMS!vRSwo~cf`rx+xgT+%rIBK zW=Rgp)=?)4Dj5n4D$d_v?gj%cbo8js1@NTf%j7DhDRw{b>FHNlo1zEV5S4!9Hm!>o zzKJM}OiiUkr6U*Tt%I-?u-UK^nRJo)T%w|BO-95dBrxW=G1F*BjR&zh^Ydp}&+E0A zHsc-Gn*;^3&{^9}jefxd;KOrs#PW>txbP%KtuUa2f-{uilW{2obxHoKT{YE12rrq z+X`KeOG=1%Krf@o+D2xl*5bw$-Q*uJ*$?_91dn4%@=*d9C^@;sqWoidW-OZ3RE1@254BK7pLHK z{i}qCp1qTzg~={aKt}+rz!C%b*6#M>mnk*ZDGbwyLKEU)Vt4E{Ae72*6M> zWNUl-KOcBs1_fU)zJvzhbJnSfkqI@59VS> z-k8^}0Z#568PQBx{OXHfy?JJ^98q6U`|aB|L*z_@AvUN&=IEEI@l7!jO)19_IeNc-0u5*F+h{(WVxb8!h=T8#G=4xK(<3f7f@T*P z_f%mIX~KuaRNuHh6ym-e*R3`(+35ei0*y6AGdqBqo1>+M?(T=+Y~m%<9j$Sg56H_~ zhC$OD5n8K^Q-ov}+W$|NRZtJ$Qi*{vXB_0&h|A!PFHu z_W&Zx1`BQH>=P8%2lHbMfI*2k!il-&zzV($(?H;sg<4#gmsbt1@ZGwKQcM%^0S6tp zY0FJI_&Bl)3qve4tt&%_f=W+`did0thnq9Q?eO@Le84nz4V!4=SB;ua#&kaVAJWm9 z*egnj+>5`PUV{;xm4ziq*b8xm`6*x~+}zwDAt6B<#r5l@Q^TqbI0R+H84 zt{q#F7G{|j4Kp-;XcfsLw`M&?NZ9i^XY?%G7+%V%Huz+JbH>3!jB+(FF&YxAllVLG zmx!NE-+3n8k^cyV(jIS@eV!kxVDTa9`%~*o=fdigCL`?_2B-iEKN!nkhCd08rAST{MPvo)0w+ICq2?65b>^$`Hnwy?P zXyk8~lFDOA%N@Ib!@amGAmoOV@-nltlhp*{P(mFhE)(}iI;d)GBcd;agxxLolHM(vYTe=b({`fCG3prIF-6q5PF;|VLM7i^ z#&t{E$=snAtL>|s>2z$5pr`{11=OWft6YK_&w`#W{H2xE@mAe7bQJSYPJmW1M^*08Q#mNCJjbt_LXQ*^QNn)8m81T8O3@L-v6JyU@tU6N4md>v-a_xo5abhDldq1kW$en zldn+!$#|;!ca)r|UTBSyu-X1RNyk*oqn?#d$wfb#a3bGP!cY6GoE#Qc z8VrTN58eQmAo)YbH|~P)b3tI5Vpnw2KcKfbc;E4i6xK>|wIne>5lm%#CfH{KO zX=D5XXBl?Z11QU7#mcL;ow73E=9-PxTF6BHH{Jras!yiQ!!M?mn-V3Pls;_z<+R0~0vlz}Jf zTHGDM8gC020+{?-HWvO^Sfy+vK@Xx3CMSAiB+)sQjdoXT+j>D zYoE7Z?E8I(J|2*XfWSch$j1G)E8qbenO;6|^=fg8hL`TZ4|5Um@%))C4zihTX)ID( z!#|Zn2`qvuom#86jJ^6$v+jA6Mo{qYGT$EU`;!Ij?ukMCxKW;-POMKpSgN{2v3ODs z>=bg&d`wSlI2P9LOkZ*{Y3jGmJ3;-;)$jJIdvf|@^4O;CY_6(AzIrdG)3hQeC~KCZ4@963u`Xay6-rRwf+kMN zvi;F;o+}A%jJ0-c@Eevwg;DJNy#11e*RSahSY{rpji@KQ|JwY&>+uZ>bo7Da-eeujU(>_^-tqRKs8#NIR0u12;NmI zJek(5nWA$0*S1+B)hyL(^h3XSX>R)>*#c_?fvYw}2Ur!dj@m)esyduUEu!4x6$dn4 zG~#T=V;qrU&Zfqo21Od(&sxVNQh12RfH ztFRY2D-kL!hwe?NmFzaXFWGW9V&U=MYTch{=i|+t7$#26ehMhty+iwr$iLz%TxYC) zd;5OXt!7v%X{XB$yhf}%n0d{_`%&9tK&5im1mkP^*HdlL9v&W$y+RJGJe7}R90W@J zoS8x2CbpPO1_f6SvIfNW90acQ^l7N(^AfzncokPx9wj9BRtDjDvGP-MJYu?QaG7I3eSLGwnrFOwx8C;`fu9MDD~6gWTW!Nm?D|OGJ!q;vB&54X zJtFk)zB|!WA$ut9ryah#pPuftdU%`Rl2*#NPC|aSo`?3#%yRwZ!$wc}IA+Jfj&nBb zF9}S~ZxBjey%DOt|4O^V)YGHNmo{X1eKl47?$E3YWtGpUnn?&9ZauBHB=662)aTSG z1G7`#x2zCX781rSUK}{UDgEdK3O>6k&&*DTtK4P<4snQjx^w4qxxc5+6+SZM3Ijt! zkI}|BKy=aoj>jkNVoYPO+$KBX( zM@KurD_G_;ofJ4zGO-%FeLPFw>Q_eDy^`$MC&h_J#p)jS<|;`NDS{!Q+NXc#UnwBA z=*LM{K61FfEV|pj>^tp;&o^iEhsvmo-OC(@di`h`^FLme>NOd)VM>aAe_^h??|fxY z_XV**8BR8~9vCk%n|}N6h!%<`CvzZ=u##lGsgnjZ4BWHjShOuIIcvm#z$Cd^Bd#X% zkt{b)MW?tzywuXtskg7~2%6?~M$gs#k=)s0L1(?WGA@ZTtBEr+@%+f?N$m*ne%{^i z%B*hEV9h&$Jf^Gndbc&x(VVa3R|s!2kAF8cU8fADuY1<|mp!P0v+G=C&gy@(B& z4>BWili3+#D0qI1jWNNtzvsV$lk=0MV&^skve4ssXAXm*f*g4&%`B^O_GByHfden3 zsQHQS3nu%{1-~CT8&t})qlNP8YcF^5=06h?m3(Nny_Z&IxjN#?&JU)hXV*7`C^ zReo8{VPn3O@plLGUwyaP$zH@dx>nEQ@WN$5HZYs9WAAet&8EIT1rr7R zB0pG4EV5y|#RQ52=Yap>y#>u1{ftg^n~FeOBf1hKNu(LF(9zK`GXBKU!^%o!!>3F> zY9jXFb%b8HHl@*5@ZKU4DDaBo~(rT^EL%B?xO9BUx{RL8RT zUC6PCnTwHPilP!U&&-|YH5=HBSoInhZHtKff(0Mp)~od+d0bKq(xrVfV5?CQ-_$e$ zI`YLHs3Dve=7xsD9wl_D%n>WAt2=Ojr2@D^L4mPxC{Aa|@W4I)u{yH36U-4`CKm%L zEHgVF!268FZ3S+M1H1rm#zJpGSU?~{CChOKmudk;*xc3J zG)WQ4)XPQ>4y}M|9e_LOfJT9m68bssG`+1IY*Gn&IbD-prElK6Srq}L z+SJ(i?Zc&TIu$yMQ|R&G>EMv_qNJw2xX&DTxoXPXOlHP@?uyO>%$D^3IB&+({Q3Et zqr|xEM%=xqAMq|~Q7`R|RP?nk`4FufGS^}{zMMZD!eCmmQUAGmSy-`ezUILZx;}@@ zmB%YJHW8ao|5`;^MHM-|v}BIJ?FNJU@ZsOV3V!VgFS(^{_e?TvTSXX{m@qXyT8GHs z0S~3BkEl-(LlN48uMxZZo<88yZi9Yr^TWo>BcTh#K@7C9Lrw&l$t3YL3vmo<>+7}> zEP-_c163+VQ;(5rxfalvuq(v;I37+f%yj3~)wxmQqlwJg>O=X7vuz!3uWfPru=07D zy7~`q*PXT19^oXah+kwatZ6RyL;U)hT>Q*RH0sPph)%-65J0ARc+xqecSA z_ksBN&n&<|MbX3AJgT~uLC;)pO80uxks43CV#0r8Wsi`y%860ms4o}qP4*RR$%#Ky zZxZ5K5ZMtVD6!0w$w413n)Bn;S&Ming;|c2X%B;6Tc6K9ZJeU&pk{MnW0jYTxUpif zUV`V-^X-D^o>kfP!FpMF3N;Ob^ly}^0`!d+C9V?O z+qPaoQwwG573IT>^L`4cxs_d+KyWzNnRo6&%|r-I~|yZ^KNl5u4(k>ks{}H^7)ERE(JRS zzkd0xtBb>TWCN=9#0^;46YqJitY6aa^^F@ao*uH1W6chk>~}7fnmg(|$hP^hJjB`S!L{`Hc4^nRmmK@;UsYG=^xCJQK1RCa^-MeIdzAIzM)aMr zwEX0=31^j0Sz<8N>WH-x!Bw7r<}h_lZoHFApuPCiZRE>A)XEsMfA4i=Ze+GdJi1X) zePDd`&!f|V*9c`3@(azE*N289dvEr!pZcTWHnd}J)2zl&Oy3io#jlcr+ttUHshs1d zo-wwT?<=g&x1)QapMK=g38&jT?lsg_8ohNrmL$EWl`Z4Mfsy$YMFMJE(&80Wg1HW8 zxZST`p$5Cdhop}?xnYRLlG5TB^H>VowmPv7_K0^Ysxe855w2PSCrq4&F@ilGP9L_!&5M9 zi?pAE)j9l^Hwm_0pkNPs!%tJ|^s zxR_@GKJNUqQWA;|2Wb^IRBk6fxkh8iP~_LozY?TaR{Ei;aPvw4?WZSSPs$i`o>_Z| z_!jxh;&TSyR1aHP3B7iddcqq7BO9A}jgcCv7*H?BSjmjrp=l&3(tLgO7C-B;kk+lMEx(@ZWVWeirs6@( z6lNWz_Dv@DuMsN~V`Q`ciEIk*j~v6FihsE~H}zzHI)C$s*dmfcOmM!fRyTTbykg$` zAaoz|iR?_Wx?eSy-+pE=|Cr{Y#I=~&4^A(bKY;`!k@C=Hu~A3hRf0^(d+M7w5|vC^ zqU;OING@}9^gtWYGUc7=EpY52bwd*!hw`VI$fh@$Z`@-mNU?}r`@2rbUi94K!$|(k zn=V8R3$^GxS0>F9)sbfoL@>|O1Gdpe4oW}nt?#hA{N9jVC6t`3!}3l)>$Yrju2XA| zRI9ppbOd%M8H>2Ao<4byUDlvTGrK8ZCd}2t~Y3@%$`7d^<(h{!n zuJ+Ze$GY1s-OLD&0L>paTzwsZb0S(e+a z#-(Y{0sbBQN6r^tt@!HyCnm6mGD!v^7O2Vsa_O(v{#kug7K@^tUEb%!VaAgW#fESS zm9Et%lZ^Hy9*LN2dpnt|81=q*d)?<}xx}(V>cvy-Z#25wdDAsw?T!T(lunh07rrP8r7U;Nqd;jjV8(FB?mNqtJN}d9r=^D72vg6uukcv6S{ag&5UEA-6B$`KjIUf=$p;CA z)S>`eJSdW$3!z9rXB)EVXvL@agtV z*=ZDMqe!uG(pF^+P~J*bPS$2#F$V;|@3`1|*N zOnWFh^Sr1FJ`w!*dqT+U^1AfKz~9e`deeG&=@!Ae$;bqEwwaU1B_zmuPjv7QfcsW{ zCiG{gJ&3+_>w!)hW@-HsCtfcfNe@kL``B%tYn6M~(Sj*gL@oC2pw^uCYCxF)nKJpG z{Bash;f@`WdR2%U1MrRLWB^9nR4{-+|zvYfPjBTBO9CZs$>k{;an@==i5wHj4EoJF*o!{(jDQV~{gp z8TVPtjD_q}nR1VL;&FoxC}l{x4In9Y{SA;{VP!I3u{O!E491FZU43|L%onr~1XsaW zF-$TSkvh8ydFbZiIIe%7BG7lV$&dQw>3`oHbDaB1jnl&(FwD_0R|gOY0bIqw7Xlg< z&Q+HEt6v_ND2l5VT&>$qM)ry>wMcFj*jI-`0pRP`KSJoKG~zn;LB7?^ph5?}W#!lN z+jOeuLsrrt$j_7DwD~(X1nmx{-_Bs}Q@+2M`yaj$k96Ufu$x+T|6E^V9hrDP>EdUj zW@ik8&K+&$iSY!qRe-VJpU|1vQSTm3r(u9N;L8|~0ZUW(6}6>VddPn6c?O z6_UX4KB+;0W@uz2l=L9@QjlU50r43`EO7%283Xk9t&=YGSN5zv`-+0;?C?60ROvB2M0YDqpDv~PY`SZkEq_@rX14wt2e2jW0 zk2Yj1(HCjN!4n-XQB@#8xBm8bJjxC2b}tB_laslKvQZFaq@ZcYb_fKCfw|#8a$P4V zb^9E^XiLy<91|9v#y7Tj$YUzXQ@TJIB zPF88JsvxDCDiP#lel&Q5Imi+IL*vScVd~tLdR;XI&nNQzh0l|Vlsu1#7r3|Mz+_lh zx=}m;`3R@ej>Q}UQ`mmWXIM~xI{gbSdv@6@vvUS&wnX24G*-VaDn?i-{v8a1d=c=g zF(RzT+wGuS!zY7t>)A96;A#NrcPlCitsh9G=a&O;&*F$jps6JF)VieSepqwlHSHoE z%;I)BxR!Ezz2D_7Iie^RQf4>_$fv$N-o<@qVER`AkO?NOat2!U`; zoe~nf%b3=nTSqF}6knXqDAElr5yb|PIT+$s!y&MH07b5o>I1QG8389r5k=r;Uv^GJ zb1Z_yTB>6@zxI!q%n_|vFsw`LE%nyT>7r$q$sN{=(=lF&ho5kds$3#8g&BVW$!U&L zv%D?8u##D7DlH&=c$Z~iH!=UMEsy*rxz*i>(p&ruz>QE8>^4>)+_F>uw*+0?h&q$$aNYct8iuzyuh-vxsY4@&O`w-aJOG~y_ zN8*K3v+lq6w&1p+zs~*zUox*R)r#BN1UwGdOC2N?m~57%h_&0bYTXBqrl<$gZTBnV zOmNeXfof`KxXV15kRdq@;Wx6jerC5zJ614G5auQ!AxErRSTXldT_a7}IC}zu$yaL& zTQin{7W!8NESeQMJ?eC@UA0nRd)wVC^$J?4Y%IW%dT*tn7Xp7BL5DQ_Q`j^BkNaL+ z97dR@hleC<3zh{K6IT}}?F!6TBDD4OH7PGkvWC5fWCeP}?jd>*N1gVPpnXj;J29yR zU{)FkASSbl`U4!{4VbN1=8Uky4^1O|G{>^y+g3VN_*8t#&I!<0{{FxwWb#@I(5J4h z8YM0ezo7JwQa&VPfaXm@l*n@ZsAnlgc8M~d|G3xId2!&M%dfK73)-*5y`!wOY>wNw zns(7KFfxME!($8sXYNs=uz$ZTX;TDH1i(Dox4vSzi6O#bz~5DFxz&Xd|C1-``!7Sp4{JNwFC= zfNmHbCVp**=iRZ!BYHRsXu1RMy#`)h-lVnPm{r5DX9;N^1ylfWmqQ%6?H_XN~?ncosbbnEH9_Vfng!A<`8Ds!UqG|fT){6WMf>bJ95U$KCP zybIj|#5?1~zUQ6qhulapwIp2>N`NzGzCjm^?A_WW;Vm>zLuBG`_J5eh*TGgT!2EXDY7k zv?-kq_2BF|I4Tem>c_RM-`m)4@nCZDt-tJthT>{vhyOkPM=a$oM0qO*&KZ4dZnh|P zOVLkfllAz4;6*dh)tk6({5IE~LSdxFHpK3q>$kiORG_%mWf{E;NgjelJm3CHb&;&=!!b%LYhEXOUf(choS~k*H1oBut>R`D&8K%v zH;xMu*d-#*x{jm}!ZSoJeVO+^h4FhI8ye0QyA45lP6`sm<#(lg3L0#{cYoH3eR3?& zM^p2|!Q`N}%3~*3{xQYrEFyMah&~!WUlSU1YS)*jN}h6eoq!3ZanY{ut{{PApRYdW zgwF+@3DC+2_?CK_#i1Yo(9QNRVhEMGO*)GMrTyc6^zWG9uv!#Ae&xrNNdSrPaw!VL z#!Fbb{6}s|TLv#0%V-p1LPWr`6qbhelS)~!XFIlJm|R<{zSnlIN{eWqvlEND?ps?_ zR5fW`Ui@6{aa;ec%;Nn-1pFBxc@A% z;M@+!NP0(|J3=3OI{n|{t|5efv3i&2VTW*G+*kds z0kE~gyaH{(lTv~50_G!nGNO=_2UFQi7CZ;ibWoIb9 zQYH3>DRDly05*n(M}zD%%U(XwXweOx zjghN=tBr?@nSSCsiw?QoyQr%pcryCH^T|wmx@im`-}HpX~qM5LSL+l!-@LM!7}_G|)HokdhV8|dAH%?pE8r;G5qjZa6D zWoYv2Tkf?i<*o$p(9CGtmMw6*(sAt$b!6K#<>Tou@ohH(9!KOVJ*4=vGT%UYkLUb4Ke3#3&j z`W>;Wfn!d^b|c2Hh=}S8$E%KKhhOVf>&9!u$M|VbrfAvmvyJSXzOKivM_H=>>0+66 zT{O1QK$DPZRyK!{b_$IU^cm=|Xm;-OuP!SWl9vy}6okGY1R?enm~;;lTCRic=iZ@L2nxz!wS=QYS4Ric6B?;Lcm)5lQiaCZRPdI^9%?K-%7{qx2btz)h{wXa9Q#(Rwmzme>?a}Is=|c_xp`N$Bkkd zOWCqUK2hI0>J6)kJPdI|HIb7oy!&VvQ>Eu-(LEp^m6W@Ok{%ic(wLp@k%nZs$NSi! zER#Ukn$W4jHlT~$9uUw*Y9r9o6-{n^`k--a>^zfY)uCd4UAM^>gD)b|RVWV$Xp|pQ z{IrKjPxI~OkNf49CeCuc!Tx7^j=qyoR^4oJ^1*T}i*5`UBld^#HZ8#JBBGUlhg0@U zi~IV=>!(iw#zYN=CdU?QhT!PscJqSGW1(grTU%YRT*HA_i3JuPuY#F_q**z9_%+TN z>y(zZ_RDLIpFT?lW1kVyHOYCS9!qE)^CiaHMlpv>V>+cN&mupSeoelnCUK}jk608C z^K6!;knSe%*efann|^zQ-??h2F8}QL#|(Q3x#$~Iz7dh7swF2nH-l}XBz*qP{FV7T zvL#{*-y+`#2W|4o)n^%~k4Tee^8gaPXOHxboMDr9y(P9z<{xbYZA58~sGtE+oW zefy1i;xanrCx^%eyFUt?_3-!b@PA_$6OrDq-(!}}*CXnQ1@ChBRQMG~WlDEu{X0TY zG{jVu7-4rNSEx94gy%K8kZM%z$KdE-0EhMR!^bC%zhE_@SU8sGj=J;20V6<1f}V%N z?`S?hi*rJQHSH6Ks=GgU`CRMO6!_e*a~kQ@#c)BrW%~OpZG~=@3FLIkFm(2!RssWp zp1tbta11&dQX!9S-xq#cyc~}(tCmrQV^YAP1N9=3EcY-lV0t)65Sx)MubVR``zuzl z%xAQZ-`BCVT_T@gl<}mR!PYJRtAgWjM^FbnegU~HV$Dez?Jzl@_dr`A=~3sj2DtWjP3$2$Y5R!kN!02uE zlssF6DZQ_4FS+`Sc>igT%r~^E=lqb#U&G4HQZ2{&7aT3nk?XjHABo!2R{cKWy-ni> zKe->lxIVlyXj9zwpKpSEGtF>3k}Cv^!=4$IB+jFH#eA$wX~v;d)G9oNbIA!W}Wvik>w2U-P}np z(9aQ8%n{(X`L!Sa7f1xf#DRPI>FsM-`T6&)n0H6+gHsz_BV3Bu(q>j_W6S3Y4FYGc z632MnC!KPCzR#2KcZ{Tho_Nlgns$4?$6GB`A_Sg4Jb%KPf0dcq$|=IAm_et=aL6Rr zG^nq#@_tTL&p_9-WBoLPZ_@!)HkZ}@<<;qso_kIA&bL){wD%fic5dHc`re{eQV;J^ z`vB>v!eoGXU8pP=r4;m6=n~Wk%Eg(!RZMgctp%)|gO$t~8Hi}H(d44?3IVzp{S3fa zp8x!%rRTm_R}P*`>dn90j(HDF5-TgK4xKt&zOb%%laM1?m)HDPl+m2W#KZ_y37v`r zxPKeS0H$vo-xBCG0EfKnbU4{x8np68Kb<&|&;q#fvabj|6*i^{3Jwkn3o}Pa2UQq) zYxM4rjcqU6zMHIoJ$Xo)m+dGo+c7_;>-Xfc^_w~$%U(KCZ_y}Zn=?iyD|h`HiNovKz1>uoV_6gBDcF0i0hUmso1F znNfvq%_IC2fTV8;5KFgBE;4Z4po6CFKRpu$YXv0M|G`WS^nqP!(k8K0RRqlUaganX z-06RJZ`e%+=4u?Y{Tc@JJr~tLq1w~$(jOoKH%}IYJU*vT_BQjaSz1WMEfxmbOPBDD z?dDYYTX`TG3&x;K$lp1u$1j9jx+WKIm+a!Hbx9S7?qlZS61}TMT6IXBDPiIWjK`ul z7kyGJKe)YL{F7sc)))T^rNbzEJW-~4<>`1?`?{^ZQCpya#6Oro4Kn}@#nOQOiTSYQUP12+7a z@(I+}zXkmp_K3pHkO7##@XjC^=G&21Sju627<@kS4^2sHqI@J>+ql=%#?GrKB&DC| z+d;<#RT?;;In1QFHA*<|D#tDn(7^u}`v~dy&?p0-PfJS+E51+(*Qp{mMp4VPAGf6* zo#59tF&D=LOq8&z5T6W^pKBMU#xIJtb~YzkN>s$8>4qN}et-GO&Z}t2$^m6LI7}Sj z`UJlae*jsx$J{G_O?mERlX3IKtY94PgqDu_rwWkm@xF%75v<=Y;p#W zcxe=Q;dF8#Ykp_XvdCv-V%>p^(U+L%<d~(U*VUQ}~PVhi>IB z+OlMl6j$W6$agHk81JM~P4JN!h2NL33;VFRM7F^OnN^4ku`?D$hwxmkq#uk{K zIMIym0oWb#8{A#H3}b?@7?RdD$YX6K9u+xmu_PO(AtO^JvZU-kKLuBd9cJp^el#Xu z#`as_uY7O=gw68je1zZ0bHngXmNAWe8{~nyU`+#Z*Va?u*NwCYh$|M++uddsukI-z z%elg$MjDwu%n@MZHPYK*&SkCt4R+!5rDr$gqWK%()hEzYpciDpb1Z5iwiu6Jwxhlh z_lESjEi$)6&*XB8h|D~{tymhcI00%S=afMCxzjBMnVqxc^ZM9n1=S6xJMes=@$VlQ z;bYz(u(*>rf3y^|tdhUr^y?Q%KX8hk)LG$0Z%jw0^CgF;Lc z`iX=bRLMe8QeS%*jN@@DNj*KTGpAySM2M7F$k>=D9h-Kl_}|Uf1}yp)3p&>4W7u^G z`4nH@YCB?Q&OB6*0G`rQQ=3sS2nsg#FkEri_TL>=Q)yqo{Z(9@I+A75gBJzk0K0kx zmSr^lL-e6ikyG~{SQJqdIH4gXVR1ehPrs`KU_^b+Ew;!e-?1Bb2ds9e?Pk*AEwNLq zP;%?C&$*NJ4h7iB*$d;8?}R1QscT4R0U9d2EJ#{FRiUk? z2L{etm{!@PXmx=YZ{HDTHHpOd|Lt-BN*W$-)eLO21-$I?_k4gcTaQw;v0)zEu9w+~ zG;JJqwzh}pqhSodNLl*zMGb+GW%&fq65K&jx<9IU#+WqJ)d&^W($j+ou3v=a{|-(v zo^Y5XQI(TCt0eptXeJ6$kVI$-aUo`cabJUuf%t$x8d&s^BUl|16gYFxpkY`}=1#tK_r^-QDe zEL$(a|ASZ@UkmLBf$tql3e3igf}}YITB->FEm!>FcYN3v4h6ntiN`4VE}R#(=(A>y zs2?E(#6U`gD3~2PK!>jkAnRF+-}<20J)JbjZzY`z%=W0F@)~k`*m4O(49sr_?pfxD z;`7++Pf!r_g`}RR%oCV$Q05Tzb#(=>mSQ^Mz5B68iTuWLE4nPQF68kN zR`)sVw01k&<_zAaNarBV=h@tdQH$q`_@j`hX1+&FY2rZGZ~L|WoQKT%Lwg}|`+eEs z-QZT%k@jM!JS2}-b3ggnXn#W4J(XT0twbW4FU@UdOPoRR{b9eM`@{~irdPQQ}zV7++)!_%=i`CQ&k@+=6O}gF}x_eX)~vVh5UM1Lkbq zdS8a$Pgs*OA5IF6wjH?Dw6AMH#@O=r7fF$eZda@KONv|&yE?4Ja_f#}P&A+IJ;s_n z;>@#2^#h(pwZw(Z=AE|;eAr6_XSi&h$V@9!T1j62viph8g&X_QotZ~|ewL~&md$5K zpJs2oYIJGoE<^J4&wr&QRMMv7KUd01DqSo?6DB8r{wwplqHjqVRuj0N?5QDhgjGU2 zLQST9%9=)1Df5y;Wp2GCM;;0{CA_I{1(+S>-`XL2GJ)v7&~h_?GQDx9#Bt^RQP~*3 zPrQ}7HJ+FK^l3g6)2UJq7yIi-i@t05ol;B{Yp?Fk;F2v`XIw1n5iR8F`Gda5Cu{wN zxX5nX_lI^xzHzhPRqs4(7;sCs)pN3VM&fFDllSEk7xLAudsJPL+8`78qh_%tc2KFd zftHUV&m}vV@I6mK_H_4qu^Y!cC#F@&{3yZma(=oKKC*kLSeVk@2cmkecSfw9kCTtdknY_ZH07ArzZNi^Y}w| zM>Z;j<;F=WYj*Yg_e6MD6c0t7eu#i-LBOp>!Q<`>^rk~}QYEhPUFQVde$ee^7&--! ze$b6b+4~ecH~619JyS0$PSg8#mHvZxM)dI~SU$))xBRQJ2eI`FMF%lR2&YHr>=uH8c;4Zkh+Z|ymHcRlu_kG6f;)pjXXrtaD-;n-UyXAj1P zc0T=Yy@Y~zfO1OO_UAr{_MKNo7+dasQ_pJD$a0F6@-g7OsWPx87|Qe5EknJ~DR1<3 zZNLMyM~UThnfcG_(#wn=8;*UF!1S8o$z4Ho?h98-6I&BFk~k>DB<1ssvBwVl|BA(3n9o~Jad*xLIcO94x_?1cGk`@_Li%MzK@|z#dm=~&9B#r-w zd^B32_a@25z*=qcy$Rb~B8A5Y57+vZ>X!#7#M2sgYWh3WT|R}6h6-*O&-!F-UqTFp z8kSv|Z7%w|(6LHWr1M?PK9eTAP`A>y?8k>Nm!vnRW#13V(|>~Khb%)CJOaOgzlqzI z$gQiw&#gm629@-A+pyNG;{7A~E?o<`osfQ*%KTzn{xSeVtaUpvoTxt(Z;e(mec)24 z^WE|4*qy7)uBO{KBPZLg(FYR(M>wPfwlhRl)`%=o;1j2}s0Y+j)G4Ow2MBnq+eeM9g>*Z!>+;n=U);{S1!&ZeiNqan_zr< z@}%*UFSWjU-=kX3ID8&IOtULhI%nr0zvS7&<>1zM?5C#9-VsKg^IK;$U7xxe6bCRy z{7(n=bjy{6pB#{YmHfV-@>k-ZQg2T|sGYid&>vnU#_z5IjXr^8jHKU4nJPUyC)8sg za@p>#59*<(hFZMe2c3 z=jN3K;wJ_B_vm)1gQB%P*Fx=TJsCAdPt;!Cq54rfoG-lif6G3-maC=m2=8`x8T#f5 zL(^-Fhxf+WU85IKp>!A6r5g32u3v--zfNsSj8wpuFt9og+k3;R*dq#8`hUU9BiXq(H7O|^O)lJMH#dg$B_nU7uc;`e~77+tD+94|}4` z6N}rg$kM5f`pI51U|6=P%VJnQoO(?mVyxsk&j}svJ*o;32UD+2RY;h9ur21*Q5V~$ zvQNV7Kw7HAM2>E&p2$ezd9xetM8r=~Yr3(@I0mr~!np&KBLAwMeZFRSh??wF0oKj` zoMa9Kr4UtNp?>|k8GpAB!LAElayv$`8t*I`i}9I zgy=9k4*?_fnH$x)b((eWCKTDP8IXGS_6P2bjefR7TCd?T)0|OB0t`^9oBDtIX2mS%6{m~w{6;drNc?r@=xhE#EGPFA!6&#yzlC2X)t;3 zmP(iN1W`mh6}r+L}AGmL)O}9@RByLzeB#4dyM5Z-gdN}sXLHx?&qdI*b3`fIZBVl<>^(Pfe+7EO;XP0PwwnrAL5?9Z?pNgY1)yg<>T3uNSqa9J(9qr-Qh1Qq2f@ z1!8dYJnh>>AHM)~ja_-Le;4i-f*6Or)djJYg}g?&nvX_g6WgAD0okPlA_D*} z;AI75#b5-SJaGa6-BTwY@;5-9@dWKPFn77cY-|OLgo{}yEs(GLN-gx?wl?!~8h3na z=KF`_XMSC^6kX)n%+QqC%V)f`lji-+gz5z8ZPG6Lin0JX6I!a;&z>0E{HSSg@iafO zif>T6U&{?{VwI715gle2{XOq--Qs9UrEYJ9Zm%sLhsr+jizkeZxBCSKmGdd~?-6~$ zB{81u+P%zkjmz)DBMIhPJP|4uF|QL;#6Js8f4oq>BbkCM;~Ml4AipXQ{)A{NbWU(t zOF|=TcAL;@_qufHC;A9@o<-;=N!lso+dQIX9e;leWr-E@vmjmHZH&akFoi?dhU1^{ zn{*yFGm}>*nKYPkcINca^%m>)ritmO$H49cDrlJFx05yAu)wml(%M?_(Y+N44S8SQuvlUpM|Nt2xK zqFXW$(c1VGXNNOSGh9jDTswI5sIdV3*Na;MDnej11v{1^-~cX$B!Kae86++1Bl7d> z(WZY{PEOm$zLD$q*YK;mA`ix;q{6@aSmn#-m-ijN=O)1(*=_miG=YJ2(;d$vn=;4V z-%O;&dBVQ*_l=j%+MHo?FmWExc-5VLkR%0mVz>~wGce-z85zdy;{2MiG7a1xs`I$R zdp|mleEU{!?k*H4*MD7h`e9<cjlk?^5z?w>ey#Hhjg2J%O#y^TSHzo<>R@ zHWyEuWrd%LPF8V&M4gtZuG>#{;;2mZ{*GgtB~uT7MVrsC9lJ>v`$eMg?1R=hiu}RK z^{C6@OzwMBgC2`s*anZ|`_zT=H;4D?T;+Shu@H8oIQ#*3cN>oF^OQxMJjJSiiRF@sm5{255~hl{6Z7gt(tvdi>d8lE7kj9`yr_#dE<@E z;(X!kL~+e^S*|C|qv8tt%AQiayjJ6;FcyEzKf}85cPh(+&^>eGftp+PQNRDmOndT; zKlPsd!zR`@*6#gKI{9ER*T(0dPwR;glUoJD+`pogPA(n1LHA7T6}zaNbih^-`JUaX zQR}V&)o+e%Qt+D9+7>4zACdRI6~&mRWGOEEQ;ePKI}zFU@q^0J!2{1PP7y3+c@L;H z0=7d3wk);0+dV>IdS6U*bie;tB4pS%(6<8SIojL5BmlDuNOCz)8vwxptR0S16^)PB z-Db{V^igz8<6qNk>_BTwYma+i>Jf>$*tV2%H6e4MYvx1K6RdJi3fqfw*+p9!Z8r}Q z8~13wT151G|0-AaxUIXbB=7m}eKU;_J%`z2D2mR=nh4c9)HpNtTGvnS?zMU^>~pIx zqI;^iuX@GtIYZHdk{ERBv~b`sQ!QL9qRudf0cZOra%t>WTamwY(E~`Yzw@JmSTL zi~Gv=dCIv(9z3K-zd8v?x1m|1W&EkVdxWj+EEiIO6=>;GMGjgGF`7nqwgWB2Y9~i1>orC;kJuWZxxV zuA!+bA0Bx#GOjp|xrHMCXO(y@_sB8OA2a;y9Jm2vy_%(YbTL~uSo)^C^R~8IO$zLk+oVwK8 zta(hxrNG_EiBZyXWC(eF{OJ>4z;mxl%H)0nhm#1td+Ox|!BCk9;Fu}ZCJYGHi)<1T=__X*r zkZ&;pcdL5Ns~i#xd6lQD-a9XQT(`UU=d>x=yQWJch$I8j#3gR6K1$GcM!y&by+EpLKt_VtT=J16ftH&vIaggFpzXKE1^WBZK zF7tgp=lm+JKW~8g(Ne6EY^;1@zx2>=P$+W%^w>xgLXLIe|>>^Y67}4{M z-}Hm9(8&F`KRVc6&$d^>XGoFbq^Y~p%_p(*_DNS9{eHc>C{=UJbfb4+cwBf&_&>S) zD2X7>G6T_=Lp%=DHnxK30}vs!g_DqG0~|g|(`N3h!uKzu^-IAP$6YJeg7tzyNEedK z>IP2_{GgK^SPiK9S<_IxyDZZKWMWjdrnL?tAUTK_r6P+r1M9l^2oA!Hz9WFa| zfB#yFiD*~guATc!0PX_ZhE}WwDvQ9f{zfxM7XViyPHt+%$kax_&&=&-hhyPC=W{IO zZ=R=(tX`WH>eF$JZy(G!{R;fO%#kBT+;mut?LjZI>kB_7BCpkc^xiTlB_vHNMQwR1 zpX_JUmcg9{%yIYc)N&t)z2W5O*wgm)&9$t$u%^~#>@to`)Oz>-J-HMF+@OC8U2^CYNnA z7aZB)!gc0!@h^>g1@xbOF~cL(EL!mmAx-%r6pYsyQwgp5Bd$V|LJ(W_aZqB zOKOpzn{vnn=fUsSG%S#p(iA=Ix}

bip8u$bcbJD7Pt}~kZFD+HMuvvluEI2$DdDQv$q(pRP;dgdvHc?3It=gh3p$>A zSE_6}n6TfKFqt&ya99M%1vU@K>4l&+{uw z_zv3Rz`=vGY*L5$_{ez{9+sW?|NSoAMmrq}OW*^bu3(jN`i5bHZb3||b@JqK$_l86mZ#}>yfD7N;vCq&KkdZ} zuzyo2ad0B>{4Y>c^ZDKNop3(dOFo8J$`>|JSy_LS<6tD0JKQkHVaF)I(tvdh>%+Vg zF?XiZF4xNKxNd!?N@nIFAwbr_=Ac?3a^c*-hhU=-Luo=Hlkn~Hs#}7Qv zUn7=gn_-B&K3>_g!!(QOPp!y5WT$o+fw&EE$cV%-VC4pjspBOJ5ZuiDw4l&(Z z(oPJyl|x((*O0gPE*>vP&Tt#+KYYL&H4#RusCD85;>zS|#L#i}A_)nJ;YdfcYktTR zr$dUz^qsK?{|rVDLk1o7=w6dTa2XH|!k7zMy&PI25VlT`_~Bl%M4W{p@fri-y^*+g z48d9$2!y1h7m6`#UXWoC3l$`Qo;`e;|FhBI?{FPMJC}}dc16VyP|hGHeY7?oE@IqNaMGNWP9#vzPOvVBI@czRg^N$z4>ov5P^-+=0Y_I&Rnk}>JxS6x6 zvBbtD7n0d(!jYy%NdO~B)MsL0S-I$#eQIS2T{P|&WePk}XP zI)es1@%#tu{98rR9%2{Wz1ze=f8xquI-@0&);?c)ivikFclg%G17UM1j5J94n=ZbI zw+y%pSsh0503>sy$s6I_;ipcYK8=wKT6>~=d_w0;H$DSdY$zSV_?z zS1=udVdZCxfqZ&obktTVA+zV}y^6E91sdr`r{^NV(#aGUce_)+cZ(`JP`0Eo$h6M8 z+G4NBXSwJ0kB^9K*OVHXovpgqus8bI-|W)uVJR?0!-PyqANO9gb3V=2-`^il!KrUS zMi^hsOjU~8t8)DzgCnzEmW#$=MkI;pJ(92mhlfqzCm_X+=F0ip4!z3nc4QpzYyUad z0)5r1Qv#TkW~||K3BcYePELobV)3$*whw@|2wG0#%udP_Xn5)ct!#@UMq{E^U&^pE zh@SWRKeWAhG?s7sKB`E_@RWJT6cIvYPDBzSvqUH%Q>GG8?m`ryWXKT7kPHclNJ3JC zG9*Q&B+A@CrhVLfe&4o6+jSh+mkZ-^54x+lRUMuhR)3*W1?B``BS z$|C6Vw>hgm3RPNj!<{NA`;C_@U!+;2sr8M?H62hNBmO?0z7%?)?ty13`jJ1|56Vn{ ziWjB$*(kpY+yxv5wWWFa>V%Ei5krvUA|0{Za?K>to%Y<(2>$uojIE$f`=fiP!HcW> z&wiR(q74~1Rb7sp5xa}jU5B0bFI}p5!xR=ex`&@8q@92)ZV8p**sxkrQ&XcQyba^< zvquaq>cZ2iF1nOig#tI+q&8Oj>tKFCZZ3A|e)RZ?i;3d3mjt(MqZbObo}W$@@U||C z$oY23k@^IqY~C&^^G}o2%fU;UzNdAye6Fy11-Jg$y}Ix|oz*s7(8Y@S9TS9sXF**8 ztf&A+0$SAt*5%CVwl>R0c)PB=a~7c+x@y=!#l)Lces7aY^cRYsaZvGYn}(Sx9<+$> zfuu}8q23J{8d~><^Si*=II|qbFf0oZqZPJriIW)P5DbOtPwGi}n&7Pl{9}4{hDMs4 z)1ED0M(?TB)8E+H85`|==i@ZX8xO?~$5jau8itjl z^@+3tNoQVtHyT+_uXUa8^~R&tgz2Y2$NKdNOmZAtcKZJBl$x%%PS#B#rjKKE6XEi1 zW+t2!*dkV=51TlWm!)&$ND>5w;!4LfHirax8fF()d+#=_no#@X<|}ED?~C5LeX&1A z5%H7}e_<7`K?i6yl%lqWRVIv5C#EYD>aM^WL?aXzA8$^8?N;t-$5{#=cWP>CwBKJ9 zO(TxU8ITkpq0mKJS;qQAhE>`I_9MQc1+!(-Zo^{JH%v#_F$H0gVRZgi*J0oSQW6q( zUH*b}0-T_rM1)QV>KCOw(?3DCnCh=l$D|g+#Cb#+h!-IsA1!qg#tasN6BCfVDJ?BcKRjV)xC8}Jt|E$x<=B*lrL=D^Y6JvY?TU0tGfPv= zP&LlCK7ywDB+*yplAPm`JX`^O4w4>)QxTMim>9*B79iV7vKTvqOFX~O3-8_tc1cXZ zV9B7Mhc}G%v+vsRrCP%8z;j`utq)$eAp)<+$V7OakXY|jf4{rJ@bU0zV>A716QNKb z0Vu+(5j`F0As>a2EbM5{&xFH~_fDmQtOGdA+PqWi2cMrX9;#}U?EL-#eLRSWP{AhER4kuo_kk(+cgP2w2WO9W}ieul6RYM^LwVFnAN zRk~oh4I6N8@TI#!QlVwx*Ef!9uunqCs=hK4gZts&=*Yyq(*eXeko|CoFQeLlaFyma z?B=c(1{Ri>D_3wzeF5Pt5IHp1DHy#l5~S#-sdC4-fF@mxL;-XQW+65b^b&IjBs46viR38$z8{Diy@v}n7KYk1uNnhaBm1@^Px zegREH8wBJ>i~KU604Vdmqplo@1sj^=^ZFmH1U2**`Gt_Z7eaKiW;v`)Cn_RB<-%V% z-B`k(Z)?0>d{WZu5_gy#VUb}T;u0m#$GU9BLO4`!UA8A}O;7K~^T%uJ7!EJ4p;FpC zUBY>#))QZn>b7gNz^Tj%$;5y(b1Yo!-jM@ z-NUv!g@x~;HpQYSxNmTp?+^IUkP#dG7B-%@@EKs3o4}%r)iiROATD8Tj1E;e2--Whgm#b~Mcz30Twfw8u-AP=KRjmQuVF}N_lIKuZgpPxLJP%~hH;a41K zbaE6>{iiawKgi?Ex%{8Dj<q58AU7lMp z{>@U-I#*nK?D=y0L&VV-KIPer(!CSP`AUoDGTSz+yS#ycgHe+A>*NsCz_}Hfq1BxY zNv$zPd<`464P?YuHW33yrYjB{f=H$+wWB3>#ZHae6ep&@{BMQNM#}tu3+J{;H%k+nr+p(- zN7mJE5EvmkKYaesC%X3emKGPa(X3gK{x=qD`1M~L`arMcRbJ8Rc)P~r!pM*c)34rG z(Su-sl5F+Pnfbxz)#&>Yi`%$I#Qvx+hz=)GtY3X<`OGh%uu?Iuba3zC(vvz7s*V~4XFpXokR-*OzecEwrye?{N$7O)Y9mvl$10F8z;kt&7mP* zbQp}!*4d8m(4=0}`r3N2vy*F2BTe zo$^XPRstheXU`Jnru5#5aOs}VKlf&!q_*u{8@+PHiY|4n@bB2A_MoEW^_}avUNE+^ z@K~!nj?a#-YF>A7carXDZgY#R;vL47JM;8q-X zFEeKR<`YlkDhlN6}`600m48s?d7U$j*AL7i1x+q zy%if{`X)Al6|M{#-9O3FiUUvi<(zc@XxdrS1-$zH)^@5o(J_(z+FiSIHJ3)Ht2+8@ z_%>4g;*R{#T+$p~&5UiJpQSte_R)^=s|x!URnD_t)2tPbD==xuou+9po|KlMGHf{X z!6icBIxUfWxB338Pf2QybMoBDp6>_2O(C{4**(mcB{`2|4y=)lAjt_tK{{ z{=8L^o0R79SK8vDTBNiZJ`{7a=_X4kOI^Wn6AOtip^WzPQ?k2U9!yJZlQb{UOC4t` zz(qO~=r`$Q7nn8Vi4BSYq5=O7ZMGLf=blDjh=V@Fw8NAzC=diU!yUAH3%BkI* z*xEwdbWuR=#@6J!Ga+)iDdMcrZnW*s6=P?niIQOBTuXNK!3O`CjZB9AyFME(cowib z2OjwSvV-k_uJTxDEIq59n0Qj2O!Y+yJh)QOS~zs572wl59QpTp0#1Fxyf>hNzkjA` z&WxEoxHsY} zN|{O_{RI|ZEvgx-ZQE;xCmooKGBmS{s|We)>oF? znmV=OI$9A&b{w_qGQFj9GsNoyeN~d&=_|X8Fr}EB#T~rEh<0~L%C^$W4=w5QlXiZ( z(YKeID>y^9#gN`A~9S?Dp%Gff)Or)iN#(?PencWME2a4nD}kpLIUeP zZ;vSv3E|q}cqWJ@oPR|+@&g00`@N>6dlr-7aBAqi=KD1gy1R})-lz~t*(a+#TNKZ` zZA%DO==Ix=Qs{TpsD9=PGQ53wh=+N`=W9_`bpJZF{tY*bnsh`{wccFyetY;KgYuTu z{0rUp3;!5D9qWLD)id$4LDu@tZMr}TycMzkM7yAk{>?NO5s`#x$=)f?(efU{bZ3~F_3Khl`N;XD=LX9 z(XICg+i{&XdY~Y*OOvov3f^^n^;Dk*U6S}sYGtF#OEZ2VK0()srqb8S84{N3CTm2$ zzOc8rCgEUb5z%Hj^hqneAa;N*wRry5K$uDD%lY-*r#XX+UN)uO=Llf-He4EOZf&)N zI*XoO6C%D-YwFl9m)TGlAUa1#Qbrj`o)Tg{sJAN zIBA2?)rfTspZ8rmxYLUQ>;TDYhN?vq(j86sjHKHoPDE| zd&k%}CR};{_5I-cX(WBTr+L2-9JG5w9Lhia0mu351jLY`G}RA?&ky||=75x9DRTH5 zFfIV!aL|HZzQlYBSJ=8Wt%B8i_UX}x`ic0JH1zm2_?7PO=^6T=d0Z%5DI$~S6R+Z9 zMGprz^P=} zv2+74EC$d9@d7?0KgYW^`0(a3wtw2yqG{!It(kp<;|c>q=(>fvrSryW{Qg(9vWogR ziI~u_iRs^d^MSjcUfBHX8M9r~zTx5Nb3a=LJhW~;I&?>JW862I%JsqP{VTPM?y@^a zUT#3v-Mgswik|w%8l^hgo{;1Z$u!m>``m?Osn%0bK{M&Bz5NghC=3pBQYddxO`(VZ z9pVWFy$n;oaD_cV(SiyRWUlK(a7J20g9W(Rqf4a@n;EPMd3*XRzW#II9f=ol zN+BgN@i2NU;B~cQ?ZJkjV)54+7L2E;@+TZSc;Dpq-op~&zRbGpu{Wb0a_gQLnH-hh z5MzDGu&KUwqnJ+W*43uCYkDoY%X~pR;j|AFXsL2Fps5Po5ukY_ClH2+%F582W^UYf zC6)LKVgJ^2GxIlhXA*ID#0aJFE!Tcw63y26}*0Xv3 z_ob=$yN7eiX+MW=?xa2c=+SK1x;0dH27zx}66zuLUZ6Ip0=<)pDvgGmvLNTpphrOd zp8?%}lQ(ep!)hRZ@W0_#i2o0tq%|c}k@pP;k8$NRRY^7b@QtbtK9zXN)#y#k*D{p*dyH69J>L~M ztOQ|fa)yj_+Js8Z*csZ8TIgo$Zw;y4+wll$R-Y!I%Ir}-d=e9{DJ^05ped{);**BS z7W|fUqGY_@|I(ViTwY!tL%vwjjP9FFo~yl6d+Db|jS(B>Ar}-!#~A!1-}u}}k|n~9 zr=DQS+e$U#d-L?6I#zc6qS)`}zgDKU_S~>b>)#$P27Tzc(&?t#p820Bc8AV4%Q7zF z?`T3%Yh=p1gF zcIN#qlVYi6#aXDx!z^b|?i}W%A);N9A#p2F9xo<1 zxMunVpgUNGL4E@-!140QIrPEK5VC-qus|4Y;n2jy#AK|+CC47K2k%+6!O|M{p`*PWAS&5T1CkPI1SqTupbQ4T0ApM9 z^wata>v(qMU2mVa#_D65sjQ5VQ3>BFQ;G zw4hc9$2$aR^4?Yl`tg0lxKq`^!`+Ou`)~k&!UxXHX-F_dX?Gxc1Niiv`n=5D%ge-= zRbE4PS@V1)+F>tQH8UvpKG>PWv>+1t~9qiS&g%K}?cj5enYyIN0aB&Uw%GByg5xPB- z(wzR9ZT10MbWN5N`MT$S(}x|Omyu0rq20zLcZ_ZS%(>`QsezD(6*ft$QpehUynb~@ z4T=Or4+=D3f)?nGY~H*FC=5nL>ttP|F(HOoei0#|K|D?P3xHxPpx}NBTLTaPf7`Ab zpAi_~gA>_+RVEP+b@^aHrRe;(UoJkWdPr5lszLF8F43zc23@*M^ed-^y_9fLHrN+i z6*`r2vlLKWtRiRB?h)xhz>jO7P61UeV8{!Wc20s@w!kiPCebb^!zBRYqJT44=;^)v zGM;C;-WrQyah7?@4I)ANxdP*R{alsJU=a}X4ddS{Ms%((b1NZOW z$IgJCFF@?K+^bh1ABELJ+VG<12Xzl(h}ZKePQnd-oD8Z*sdYU?biujr=@*X}Qfi&8 z8K_{Pl3)90&vhLUllyF|Cc?V0jz?6zRQWad)jaMf^kPMTM2CJG{Jv9nI_d#w>9x5_ z8|>Hv9+t<4hvd!MqJ8Za7BJtz!ckSFYGbx+Bk?aj%Fop3H7IARh`yhSkqv9NufgzV zu;2^(9V>&Ob9|bwT+3z?FNUUN4vcX}z4Uyx-~`<;`1-C@F@r(i|_goOXO_u$6Tw zXRYDDN21Jas%G_B?Xg>cx5+_UlTL}q267-6{p&{_eE}$$p!avy@9JK5)h*RGM97D& zmyP92N5dRWO>n=JV_NS_m0PY83tfi!QJh5fM-p;eLTXXR4{Y@2E5}>m`{$r-jSY#E z;I!Y>GMhq4y2QVnn* za;#F_Y#yP};EK|&wuK?appVxb6{%+FBPHZI#o%t5oQuij$h9FKfjpz!`2{8j`Wgg= z^e3g6=LKR?KGU$+Wo<=L=IQBKW@TU;N4$0*#}Z{^!krYTR1;gABw>cKeS3q!7^E+8 z;T{OVXm~+!Kc!_-S}@m+i|pdRLx&2DEpArU;mIM@$QSk`*M@7;rV_9$f$*bc-v~s~ z@WJ~)UT|?RCJb6YU32rn{I1W~UdW3Bbr6SLzoBrcg9dw7=u((x<5>T%Z8~+JJ8!u-GixG^f2DL9O=8%ukIm5P}j>= z$RC1h#^PdlXebaioUvT%x!|(rK#JFSdr$JPX3^$l{` zH%^~Ejnnfd5pbbC(!;5A0i)?JI&gndWshq{6}-nRDCAB++%cT)g{0FghU?+On_&cf zz*KdD@|Kn` zMqY86UX6@I9LecOy zGyEL)(PGf9I88Q5A2N=Zsl}WQ%8qbjA)ZZJ9DDEe{L!A~l_9jAJ$(9ZR8ZF2l%%A$)D(ch5<>3}__|8vB^sHa{YwOKMla z-bhXDna5)~SBsYJd$?oBoslV^nLK^x_hV$IC_JmD>o(wWl4Ot9;TI^HsP|zP7C}Tr z)HO45@1*IMPtu2a>1zmdL{I`IpNvj#nQy*&{1z!R3CRN-A5cJD`fF-xN(!(*{SAP+ z)R7XJ1LD2xnP69enFB83IM_q3uCAzmu|m+#i=x;cQOJu0j*WsZ^eq&waK`oEV8_nd zt=N6og+QYmP-$yhTYq(6d8F`hq$@$_X(M^E3mD5rKKJA^J2oU_@XSG8CYAIt8uJ@X zZM=KMfd*tHz*hT#P@wsz%$ei!8r^Bq%@jcevtR@S1VM-z?=I>`M}ndm?lqepTOl@Pum=k}h_ zYJg|c8nL}}W80QJruvF5KYOoOG8LgM*`7Vd&Vd+-raulMIB@$~JZ_Tb(tPF1Ze!zX zHIkRQdb4KXEAKO(2eSj(T3R%^v3N*NwiEUHe_7X_=$eZWm6i2^t2)_3tMv_5F|8ki z2QYl~Mn^`@9i?vDJ@wQSUTOjYqyPmTDV~6EC{nY{JUmS@rO5M;G!Kqp!$7i#Q|JtY zitx3Q^uq-DA(n$YX%j0eFL)@g#nK0sAZ`6WG%w=+s$C&%fJ8!bTifUcNWLhSS#939aYlTqC^E|84;vBs7ORn*uz+9y%#3|--ofyK zbkal0iPR}xrp0FQ-U<@Qo}QkaNpC)V`qbyRM!xOPhmD$mV{ATGy$p-m+-d*TZ>4`E z!e^#8E{?6VM-@UDDw2lr@?7d5dRA7BcmB$I2-+P`tTQMLkd+M%4#qd1o+Nse6C9Dn zH`(Pkkt>0_L5k6=^Db1H@!Hp}5<(qbPX~zMzp^TWtazG4%bBGeS}fH3z^JUzu>hE6A#p`ItxN zPInhtAAT~(WInf2`Pn(%P=jt6XFlj<$k!9~vLTWLN5qy5GEcomnxOZXE@78k_1m!8 zzS#1S)%K$<#l-RY9nv5OefC_w!x5q#7SlUk`pbT$0TyX08z#HDjxDW?&;K^Ac80}< zUDFv$0#OjD9GG_O4|2~*HKR11fHWE@88zkcp?{*OP$iY;+ea6-Hr9LO!99{tY>yIiq<#-F~~ z*QWElX6KOvU*jwQbN^A)PRe8v7+~mZVe@hX1Qp&y4z3gnQa+-cbcE9*KPHdAW zrx9?YEl03VSo-!IE<=Dr?zIdF%J@iQ@S`&Jtv@<)2#i4Fu}g-W-Tic}lIKbSRv^2v z33A_6LsV@h^=a)A)Qx#QB_fnI^3ZG+k_qu+Js7Z(qT#6p?Qtm~Da z`PoUbL7<^g3;mVVYc-|tGSd`?CuD#B>brwJsAfZryu2Pp3J*T!d%AMq&$rF(HgQYz z0cAHulpUMmkPkZ4hcO9U`1uZ1xGm@<5{U(q|D7T`0ZyY{gN%$13S;aXpuEpdws*Zc znBTK(T7=9y?07<-U0BV>O9!fit4fHzz(C*A|JZ1o+;^MqHV+>EJRWGE@P0pRJoacX zS@eZ7`|UhkoQIH)j04HdHHgdCNnt4?ObI}#pJeIk>L!70s^0udu`ZLXfVNo5t=Q7* z`nt62u#OblA}|ESrKNut+`Mk^f2YfF60Tk&&_SZh0G=|se%)G)n_FY7Zj<*OF>`S! zQf7XuifREVHqQUuRc!v&4-ju`*yz&_#^1X20X60UySdE3lGrL)RL(er8X6nXIcTvj zJ_A%_cBkR=N~J%zJg<-q-~}pF<~wJI3(mZ^_U!6Bm_M^jm>OOe85w4L`O`WrjMyl3JY6jBO3TCMzpc?$k57I{!>lJMZ6XeEtlVV#>^D*U$;erTzRfp>R<{ z?#IZ8!$z!wWzsO8bP5&n&iw%~S+~<4c*m z*#}-WK2Pf;(#?+_-VF&rL;LIT#N6U4aZpLC<0gWbDpCTpq*r+512b06VWGKUX? z2JCCPiBsOv*ZhpHfC^{+P>LAzKsOXyK7}{VIFA(G=@(W?x{|j&6#e`oAJlC05)!cD z?TkbD14ceX&^iZHH2QJ}f4=lSdiJb>1_g)luj$_Maj0nug(8h34F9PkA&YfTNI^dU zgA3C}_*}!OHjsJ zK0Ky@QyX0dkX_O+)PX)xO3PQ^cUwYJKfU>QbSX<+hOMg zWY^$sB6m_qfySdWhQ;C84z+vaHZq#I4<8!c>rm#422&dJ%AHB@mr_C{Nv=J}_nmgb_H*lNcc4f{U z{A-9X0ep1pi_5FzDZtjNl&})nWWJD*?_rL{hrB#Hc@;z7zr*Bg7V@hImNem|!NF^9 zA3D$>r_MHl=K{m33Iuta;n~n%l4Uft`4t%%PO0>tx!dD@*0`-34h*C`7q+n}o4M(C z8s9Mgg8uIit&j0h+qT~r0`d~^dBCyn+{R0v%Qd4S{TN^bW zqVxf}iJsE+t6R|{y9F8yDJFIF-Z~)=?Bxj_19Vz3tkeLwuV=y3%JIf@0Xa6)Q<9CF z3#<;RXUxyyuwV;)4#VGIHq!=^PFoZe{ji+oClQVjKAx?v1fKtMnbX!1%-(p>n5d`` zzX9k;Dod zhlncC-;Wz=^VGk%G76f877H@o5GKP)KFnp&(vY$fTyKbfseDpLrm!FW}G zUP;7*C2|3^%D=(tJf&dm+imO*Jj2@>4Iy6jAXPQbJxWSSJ9ZQ|jsHRUcAiCf(sC{4z7^0(y+8ijfWq;Bju;0M)!pUz;G37QqP^J-qXD-PRJyn+S)xeA|Fdq+zZ za}#oou+k6}6WhIBK6;o`#p@%oS<<&@X1x9M$vX3$5nFc0nCc|WR-=Oj7T=+Ltxb7> z#LDKV6GLYdd=N3f3=E*#T}EYEj$g65%foa{y)0zs|5GPJC4iy=lyhXiX}*W3sh(m# zSKESj)DB$dtsFpnPgn^eXhVIWqRM#^?v++VAu}^Gx!91kjh(?8HMO;cf1{N}jE8x5 zwXFc`31L1Al&=HnyvBNJVcKJ^f>OXD7h}G#FK&Eeynf|4e9{Adiho%+rh6z`D{V&rW(yK0<8 z{%zpD*ms`;r}ljFHc#YZZbS$#1#p4`M8^7YL7i{)#ZTw<1}siJ&i~8v{6OX#r(Gh- z5LgNp6xnnYQZ>yZ)0rDoMZ=C`Gr|R=8@SRUR1E7fyMwKe85N>bg?q$y98e(X0*=iM z7Ry#VNx+Q?RAl03m5AKg~Coo)NE1d`jj}t&8 zd{4_4o(!!$25Hhpz`1C>Aj^SnvDre&$2%*|bYm^ty0PdGh$&yy`e&2G99}>4!nI6O z#)XQ1g9Zg$fFT;9f-7uCO3Qp>>}!YM2kv(sqqRspI8wS*NJt~|-8BLAAVfBX%XNL1 zXWg-?!G7)lv`W!LGI1>`>T*uY-=d7{a)$Y@Yxp%Ne2qtLqy3rR#c;u!YT%#2?0(@J zALw;s`5JrN6_zCF%1`a9SMZ9&v5Bjh{9Mf|B!@W&5`xmws;DyJ$DzA{laPJ^RZb|B zXTE#-`=c8oeVF)z66fyMQ1jZi3_ z*QW`k2S?yb`f*_2nsFA$ULa5+JiyEot&i1y_y*q52uW)h^yhEJ$pNzez`}S>~)MCozlv^2qSYjeD2oNq1i6=+Z@N<0NK(h&T z%v`~}a%`2WC-|6&vrF5F;w`Cz^Rg%5q{AHE=W&vY)@tj;K+LS*qi%9XJD<0xZ+UY* zGIX*=$3Nf@}6FUZq>KX1IA_OT$Y?nC)l%kJwsl73;xXiD4N85;PB(+VVclO>@j##@U+OM^TdAaWg%$&Kx22b;+g}vW>ks_?S zX?4|zs9!Z}?O5_N9I@NL9wb}wr;N%-pcmK+DQ^z|g&Y&KwS8bD4xbk*L{HR%h|sWS ze^PZ(J`?lAd&t)^dYFLXvGx&~;i5*uYiQm=t-p%cKE2*ZXq?$=N&VOJW?!tBm zl@BWs#43~}(I~-ddU_h>P%%nlCigl(C`0W72Ocp=$?sSx0o7@Db2+vXR&YL0z<98P z1j8oldfKr<+g!@mqvv}L`wI(vl^q>x@>^K193ArcTMTT!g9TsJ4F0)0`)HS1s=1QB zsiRyYi-nHMTZRpLCo)|AqEZ2B0km6*9{h|@qhrU8UH?a9_-9oABs=j|m+N-ej1#NK zi`0mel$3m;V~nDymyIp}-)JjN#7C}O3=g#b{+!sUzFq4!q$=kfWpDMGPe{-8 zinR9g>O9?g{(L@*`lZC8hnANe^CUqp@pkelmQ0;esWA!SP_HeDu)8Kmcm7q1-L-z3 zcuyO)&%OZ<-dZpuF<$(8)v*T+7QW%s%uO&Vk&z)}a7ssM55rFx1cQl*D48zto3CLA zuCArJ-`M8~pZdC(6BqBHU)Y~idra1g?gl}~&ZKWwzw*a?|6`?m)Pmr&UU+Wh zSm}Gn<$67p^E8?^=n+DR!8;d{%jYZW&PB3V8d9&1WI~YSedunDnn8QPkbA{K}8g@>&k&=k2R9T7_DVj7IKyNaPu6Qoc!^=Bd zHk5M6?EGhYu9x5=xr9=hOZXZGMQ^jBgDbMoZ(++f>-BXDUGQC45Qk0;U3(?-+MBOl z7CDIrDaJlZKdMBB_a;KH;ojFnC!6dZnHAn5b7a zkxq1z<9T+j&7<6Ul~b-0@`nD}djkTYb1Zs8CyQXcs`YK^4jR9h;PRrXV{)0?F45?% zBxRwTC<5Nm71n!b#lCiME#o<$lB$F;KAe&%Ed=E&zJ;6_?3Sf^ZAIajw=>C7N}CSg zYH80=3yUz*Xb@ujxCMCe#hg{t#VK_2?s*pZ%gd-`aXewnCrTR|=J+|h&GSqc6d8#} znEX4aq!Z!M6g@n>#^~nj&WkCTe9Y?&I^+z7tJwA=iQL%l_w2025v$+mbsG%=93=_7f9aAxdkpA81o0R$YqXsUEigPBb{zg;Xf-(}pAGCU`x`Q>lxiOtNkruB?>;(Q z3d(-mkfkLryXW|09a3Xjb#0cSp)iFGQ+jX}(231*Q9&o~=~F(Jvmvg&ck4~G zwHAoQMc}LBzy89t5iP#Sc2X<#0lqrprmHX7+6=Qzb}#}Hx!dKMk&&xCFr0^`9wvxU zha%UPGeC=rH%i-&6Ka-Jxjb3re-DXlT3B3!5PM{U&+5vBYFn15Q}WS{$WZVze9@Q~ zLmeGDgANq@Z<)bXCGPMC|Fj@(h`@t$YIyiW`KP(r*`EQ`RR=q#2cx}V5+^Gw3!e@Y z+YcX7Xu0wAjEZ2Fc<+nPIU^$&E}-wHa+~?%r}5Z#&S03C-8Tb90Fhw$K~iqxw>t$O zTDmz{5M>z56+jO|EXo*cw1GIUOENPf!HC-JzFsz2c03EiRg6=&_pPH^4Y`{;FmTM% zbBZL^ef_F==Cn+LBmNZKJ;$9#oSk!x#LxI$#qU$;c>cP&R}i!-P;r2_VwsR!hYbcD zIEVNeNnk@{6UY@TJL}*RdofTWAYTUXt9V3HYwP1r6O+=-9eN-rMn#bs4+skw_b@gxdWZ%b&yhJ8 zK~ABd#)Ij8^^qU?>;UnRBB78Fp%)-!P(7&aG=Bwyo`{QXZYJEQ-c4XOloZ2;gPT8a z3#d{9ID^_3sU;&{GD~t^z#)(?$rF2C;%;l3W+cu?N9T2z`@gH7JP_*u5+3>z^<4d{ z_tJwP5dIx`Iy=KgWtKC&k6dIlZ}sU#JEt0$p0T%qa1Z4gpZ-UM0m7P`oWvfldXO#eo3x7lWqf ze||F#H9C9tH;IBEy#j(yl4kJNkS5yg^M!6T4txWWM4^IN&~NWhT%8V5j_*#md6V?Q zMAH}#2zukcAn^#<5n8`HhwdRYpJcj$o0R5w{d2G3eKQ`$Fcsc|q)`CI<{Qa?Cf3Ye^je9i1{1?z%9J>1d?2fL2-U$oIk0pd-q(y!oo>HXDGvL0p zD6Rl2a!uXD{XUeUPzaz#>`*FP&;BCgd{dJk(M?K6VSyzkB!KaL1~Z-}_aD9Fl}FS! zbGZ7(-`Jr=^oSA}z4qR|OIj{`JBVTW{4OiE%jdM^T|0G(x&e_@_Vj{pb|k@oFfg!z z&l>V}tOv~ua_ANnIj&WdLWdbeQ2I#54iYoV!uJ3LgwouLEsB?kyqhW{kfOujvu+_EH)R3=iL#qHCfsSLc=uRTohEnQfcR9VWtR@0{ z(P=T0n0T8}Pud7}{6ypFq#O?#D4Vc{`1n8)MBK(8Fx7 z&aECEtm}vA!%VtNYOxbkHwacg5oj)<(dZoE5ctj)98X{#8BXu$767$82$weCZ@RIm zc;F=3_u!~9xJ-!s=vhEhK}r2!=DVf2^i^oW{KeCnhaToAx(^%Z>EF4Q(Lv=y1m(v8 zDH96|j=Zn>>p0!=Czg?L3DYPvbxZ%!Y|rC3=6s{t$hk`9(MY8GIeY5|#Ni%|8x#Ds zP!hhj_r%8VBcg*Gx}oNqJZ^;UOM~Y1^f!Ft^cJW5wk`91IRwBdKUGkAO`wd-8XAZ=2^^m{y$s-c}a0`XOJDx zO(g;WGj7yG(=-`^s#`oZmrt;>v6-c1n@I@xostKnAj>#x@6VZFVu|uYq<2AM?G8=r zXRngCJpOuRJNKqJ1DQTgwu4(je`pf(0eOb1`gS+i7rQ110sGgi>4OR`*44BVo3r5HuCNA zY)p0)FsWDnjy{lYmM5mf8oV#g01LxaPcm?pg@HJ=Zj)Qihx69C?4uejhoJPljM8Z5GLX!@g3oQr#xM4Hpukqu{02g@RP#?YF$rG{)nKGTZ3){LAE34grbK5OG2smH<<7# zEoTv4MGq#>tXl`Q0WB#GIMH^1Jd4+h7YP0*fN9`Y%X-{V{nzaWQFYL3V3#eJj#Hzf zia2}FCZM6ZBJSYnLl%O5qMH2E&E zwWVdG@gkKxvQV82BL^p^A&w#|fYN}-9scqCfCYi6I$Q;mhLN6r0pP_}}a z7Z;oH&UxA%P()9K_X%{qplS^018ahVmp3IjIbyUmMR5x3y(KCik~W6KpauiDz+n*3 zF>*xUE`(}A>-stzr9!aEYm6pHP zd@b?InTXz7dL>&^#9!t9(2%U`n0;_Lg@)bJa{68se{TNJItj@;cdnG!-VqMxB!U^F1Wf|c zT~6EG)OY&&r%~rIhxodW`t48CSp?4MJ8j)Hl_h^+X5r1~F0qu))0O3Mf_-~SD=L&A zZ1VWALId?tB=O)WZesGM5^SMXMDS_d+=t4l$30=vJbkG&GF14@Px@UKl7E=e?Pv}4 zbEEG69;|-rsKBOBu6L;ugyK;S*`x36b7?f`GFsPnSmzx(@;EOq@6MfzcXRQ-X3Sp5 zBcSVz{&KgC-)%E5N5>HyC6Ig)^mEz#YS0U4ZDnQUUO_>>?Sh-m=mcu$1YTlwGSxKV z?alb(S*B3f^neh`dBegbGxt3_%$M`Z-q$wU@L|8h_uRXGnK*t6d5g20mAQFWQz%;@ zOjq1?gr7PM`V7f6G%S0r?v9NEk~fx3O8*p6Is6ldj+M18`E5#dCs*8VjZt6kJeRRi zldf!CPI>D^k=N*Nc2ov49QXyUo_{ z_wV1#ic>fGBv&VY2qbg zcZNelaHE*__&C{?Tx3U#|K_Nk z&pieFP}O&Pclq*FjJ0X1Tk-b4TmN`}{AKAy1KT<0OJ>bB9GY~F9QvNO(`}2&Eu|ok z#o)%d=`f>7ciFlB3!Hz$p5-V~fAwJeaWYdkRM`l`Ac9aI*?pvT`|5I5woKlk0=#t;N zpAZT)IvF;${q#Gz!j3T@2p0L-;t`_@|7LaS24$N|lHQBuBE>%NR&G0;rV`)p#pO$T zsYct}HfVl%lNR%X>EO1{Q^iL=-ICvO=+hVSf>p%yJ?ZWoIg5T8+y1L}xgr~cTh2U_ zMI!K(F6=mv9vTcWz1?_wYP;B)ZG?7Z@Sc|PPG}l5eC%+!c?ow}0U_=NK%g6YE~AZ_IX1%RWgBfY{g$??+n6 z*HCa+>JxhIO3xJ*w3z0EmfT+jmGLy8%Q`l#)mXnzzH88B{0tjo5{>D+o}lB? z=zbzZy}VvDAw_ZEwTY^VLDut+ya^w9`WeP#9=s+3LruRn*&wK7OX9I*d^uiM^VNJ$ z&VVa#=}Co;IRm#%HfnxjOLY&ZFR}`Kdw;{O(fBiV!zF!j=Bq_R?f083qyF8I(eN=Q zp)*!mCS3y+!(QcGA_71DE-GbuX8w1-$bao*wlrv~?>ij0{RD%=*O9|}bQt_I35@!W zi{JVTLiRq9INv98l5PIBQ~e)*y`_;4)R~N+Xy%@-`x!=YH4LhE6z%Jtj?1WUxZ5*# zqjy9Q&mok2-&Q3@k0a&fN8jt7GmlYnUDc&J#1MivyqZu&h2Ifb+jaf4VsmQz;a}HP ztgOs`${CLTx5nt*1BrcfzHI3;5$z8o&D?bpV_z#J(ILQclJ|o7g=;|kS!>ZvO%Fu7 z-S+Jsk%l~w3N`sp(GA;5nkt`h+qbG`{V$F)!jD9kjXFa}-03W|cOV?vHp~cp;dg zoVdL8zjq`rFwQLMw~O#~WnV+q=ZZb|nrxWyv;@en;ow$cxKY>fP|HmmHkJ==RPlvU z^mdVVbEnr4F_ToW@&F-2?xmMCum1fFE8l61J%)GeXg*b~R^#(b;b$q)TZa3nQj1$0 zYjEN7vPS(t-R$m8_pASY{_#2a^C&t8^ICOF-#>LFN7$xr{`(LX@6(vlw^8ra zXi!~C^Y07GUBnj_*_F1_f_+aR)3SS!orK?Za9U}--fVM~jJW%6ul?L8qHA*Cx`-}? zhS+c1-b$l!{foe=0)<9{ZNE#>=s9Dybs7WbYy;w63}a8VdZ#`TY}>6hdsVjj`|Q_I z3mN+)+o1XppF}+5ZA=}!DzqaTCE_KD^(mo`%-Qxw4`+2^61UM?s#$O2_KlrcjxNNu z54z1JPj9yK?>9!)n{6Uma%^t65gU+@;sDF+fQYc*cwBG7ce#u{tRbyd)kssNFXZDk z;q*2kHXv;MYa2{z1~pPPD_w_{%hSUnm4-`xBqPINgCC)aaB%PFf zmsyySmmJ(!6{oLIrT!ui`mAqfjs-Le(rCysW_Ew~Rk>|d7`A22s|lE(3|dNU^}E!V za3%h#Jiai_uy;gpu^zWWB(43tNhk+(RFX0NH2a#t-Kdpc`0@0fqyHQ{dk+p z=z-WQbb+`SRc=R%ympq8$WHZA>ZMl^(2Yej{&(yEqyJwg()7Z4rN2T3^54}PgmURJ zu6dR0xrc0Fd$1tRyMOcp8Vw!7z!Y!j`02wT0fP>c=DR-=_GMg$U+CiU2cE>Ux8BM$ zgnK@~Zc=2vS;c`+;e~aF5n{~LE2M0Tz(v5rb#%@**y-_O7!8sgW?{he=5_5bO776d19gaMH_Dqe75pmj;NtC8afNQMy+NoDb zTga`|dFjyOQMRem$uD(ck&ysg5!EdLWC5@})B5|}ch2VK=BNRsICc+T+l8ih_Wg^- zu0y)FCBHl62Qm>2>%Tl$hjmVuQ+@{Q8#Hj86jI#W+|Yu7sX(9s)doLq;Fx37`NN`uMHa6baTmmFv)?d{p@n*u>OV2@@eQN6o zl*q%wW9U$Ucs5;J>xt^tj}^fbtIwg-NR9}OoN~E+u@2}`W409&*p7~(hjao}Xf$&3 z^YaS|kTYRwr`GoWpzFP(x%~h5;dfFLBB|^hDkND+NMvtCNm8jOGb`efija(uk&3L6 zWRD1y6iL}D%BJj&< zQfqR3L@a@c+R)4lExCasTZrH98gOCuDPM)cK}r`v$Z91wc4_T&Qj4y4Nog)SGOmJE z_~>a*Z4T-~{X;|h=%Kj1Yg$a?XW?ldJ9dPLdVNt-fA*cD>YLI9Xa>ZoaZjjH3-iLG zqEcFa0=E|kzulGZqQY4nb^pFG3;q#y7fVkGoA-;!spwxc`}g@fzTZ+DftCYjl8%lJ zse~>4=y%{(>{3Z5HW1COdGCo4n9sUiwme&i%$ZPZzJMt6$bZ8O%ToNUPrxRy2%v5 zg9q`r*|yo%)xsj7>(+y90dK8j;%(y4h9?7JgAo^;;VpB{ee_=VW@>JJ0Oxz<$N6l> zAu>3LH{6uxU}vZG1COOCx83%DUM$Dg2X?gA1l;*(`2>}89BeAiWJ5T7G4JfqD;(=Q zfjW^Y`#9u(Y~6hf_|M-6ZSmuPekjleJ42=dbkJik_5$;gsfkI~wV67A2Pg6EK)rCP zPo4t+)C_F}0=gZ+989$54&5ArePud~@38G9QoBb-!D$Na>@q~p0>4)RT8)pOR3IZ1 zz};C&5u7pLRtQKwLW|8`g^fEpWSec=HBp;+YB=5UjQi{Jb|-A+ubrE;YgkVhzlZD`=S z3eORq(e{&$l`tzt@3DN1B&?khYbt=PD-odlAxHZK4-5427#Zt*fp9^2d;$2JDP+;Y zkFH7DGze07HJxGX0l`y!U=Erjq@PX$Y0`(T6YoPW9!M_|4aijc|26GA8+tJM(WBL_ zEA@C_?hUSFR?Gp!n%2(B0VR12% zp~hR^5iD2tTKn`Wg`4D`ead#LdA2{Y*mG5utZvvg0gVvQWTtpL)PGZqWMK+u-I1Md zB?76Kf2O=TfV5(q{_y-b?2D{`wL_*AIgd8Im8Zk{rebTLAP7`l1Zb7Z{ub*wHm3fVQ>0CcKv#^_9Gb65NqHfvJ88 znW4f8Cg>4Od4Q#g^4&$A$Lm-2`}gm`Oiep>(mr^}Tjln~n?cR9r=>5bNtI!^ zC&lekO4qYEwJJa{4yY*WMeI9 zY3Wb^gJ1Uu(^B2dQ(XavRNa|rG3+A$aOi+%*dB`GMw&E7>bdRO`WwDSU5*vXvHVlt zzj&L4pX#ZV^ufbAJueF!%)HjLT;-hc&Sy@Du zQvT~wnO-@2kO7pJ@Et+jZ^%-x$A|M|T9@4z0QzB=@q&9INb-~x&5uXZ9=ApAoQ9{&mAA zWpCZO6@q#mxL2m!4lK9G-TN5%=#iz7QG~5U^Ty8~Z~B~9DZlu(JiYx!lE%$^Z@-e@ z8-Ie25B~ZzVo~S(`pop-p#|>MaB^F>mHmHC9G=>YFenHMr@2=ODb*!g}tU()C}@5W~ruc|g`|C@~5G2IPEu|@> z?lt&IZ^a%MHnU!hm%xV+l!Ty47&Zkhgzxtz6T|N%bg--1`jl1+SnL|g=}`?bwEON= zMg*5R9oPG2Z0KqVc)-`{qHuhtOzJKc+Nhv|{|@EZsM-@mqG~|Zqk=qs{OU$AVAIwZ z+lt*SP}wSh5>7Ma{r^FarM~k z$(+$n4vCA46V#zskNCgd9aCP!d>_YEiDpx~3&SZZsx8RlbX`~Iks$U(mL^CSzozKyNbc$cE$-<5c>a(i~Z-dOldJ zQ=$F19wl(hv5dO9I%J%n&G>-x4#KT{7P2j@GL)v~X6y4yDMC3tzfN45zRa}YR;y&{ zM>k{k5JPECrTVM~<*Qv59*50t8Vva!OpCq6OVIvHiuZWvaQX7AheFJc6VTn~gyV(M z=f5S*kW|GlXAiBI{aJf2c~(f6j5nOvl6HrpTvbL^Be5GA%`Y&8Rs-wDo%`kHh2xT& zTLDh^#V1(}D88vSI1^`^i3CkAEjKqeKXs!MZ4XplPk(r=f1=Ls*604F!8H@K`-WuB z*V7Nv)LL|8Pvx;uE)v}VJbfZT5Phz{aVV=5 zm<0WLoB)@7lum7Gb9Ftk?>?uZ z4Jx#2KqQF5EPkU>b&CnQV&JRME% zKU6z;XhWJn(*9RT!|Po83xE9?dKIa+z!A`VU_9`tUT;#zrX3NxjP88=H~!W(nhOil z`T=xUye6nANso*xhV?hT*>5LNZJ_4=z?qH|PCs5Kirz{z#3$Lr4K1FQFf%sZLQCB+ zK9($ctT8vK*2rR#Mtw|)c(tnd-Y(($r-xO>p2a_@!cCZ|$UWNs&202e;9@8G&~qM< zJI6AtS>I#qjJX-IKzA$1Y!#|eIeQpr6m7_nCq_>-BG3W%hhq_fjxg!?Hy2;}m4|I% z)1%^+Un>$BE0#j_#Dn(2VmsLn2U_DaL;!srrnTOsLpa2WK*9LLu&|SXvhkLm6|!{% zTooIn7$}_8YS^J4?XcU%?8h5Ppl~7q^ht&KoXe5!Mt%QROz`hW3GMi{x8*&W{8R%4 zcV^hs+c-`+H66UdZ%R9G5#2TGAgg$EBvI2mN_s zD9jzIUHP?dO7)3hwDsN>AA{3o=ZZ;+W5#ydHlDb)bLH)^-mf%j9P7Qp4I+HY>1h+` zZ;dc4YbtOvQd1Ea8QK24v@lI6SnOok7^S7gHY{-xsT1)K*zeKg(oaD_8ELVPBUdp3?X&`qEG0%EVR;A)B=Anx*4FdW$8>5*qe0s?C z^}$htt66@R(nUQ}zX@+4_>`H{e(hJIwHEnXy;~*yFg5*Qtyg7F&JcUViw!HK^A>*m z`@O}JKN5Ymb=N zO{UGui{3q(T9?e?pLMLIXsTE+j;g3y2(HDB`ht;L1{? zV3AuFm2L*TbPAa>WY2VaK{dErUwE*>?dawhniCl+bLs%Iow&K}F>7k5(QP z5u4i`T{NnXr*}_m4~ks#_KUF+)1;j38otPE^1>r^R4vB6xo6tm1bMk!chFy!;NL^l z>~oawCh;Hkt|gfyGhtQkE1x0nI{8+z7=phrXLx@ZVea-;TX;ch?<^_=n^h!dPP|Wlvg2wqotZ1+v2SH!dp4`dwSPVad{s)K%vzjey^=+O z6#SCX-x=Fn>djs>raVqp5@)@CtUzvhuj#Y`;ldCYMfmZ;EO=088>akxc=SKqU(Rpn z>c~@(ps}TokNY#+KYhKqYHo1VQnS!q&B(QPdzjRk>fa$?@O416K2mq@Ez&{QR|pP4Su`tsX* zrWl_04=82*`Rcn85oTYpVP1$3Vfr_|6D7)0q~cOhcBmv?oQnBy0r&6YjlLoc!QO?X zlmXRT;~{N9;V8)z8HJr~(u_s_mg=2jnqEO5G&!rCvdx|^3Z#~tn~td-{@Xd7!7Yjj zug_-QZcH6Sk~lRcIH}%hIs+ylQp@_xAuXG_8)|Z#(S+Zv$cBcbe4Qg;BQh_6i;g+K zM9nu)Jh&KR6|;@Uht#(}0a1CPLa*(var}bg!r|7zmcQlC-Vk{OJOyQ#d#tIlS7F@* zq;H!)O8a!P28ay)-^(i(rGn~|xO7V%>IYSc$w-H~AUQ6hIj(N-UFc3vYAC@Fgh~jj zFD5YQldCC9)VJ~MI}D@Z^4<3QhklTYD!E%|!W7BUD!B0$6{UBoXtuCtXhzeJa#(p( zZcv#fE;U{@Jg)A1iqQs&LOu4q`P=x`>r0XAX75wqxj)dPVCXzZ)9aMlzx6uxW^?7u zDr_4Y&D9e{g2;{Ttpq*sRsxBCbqb=jB`3v492DFtQn+?DfXsQv9+bqKcoM?V%wvAM zs)lz|`F{vZ(s<{LI*QR*)38_K&pk63l@&Rrr8+h{=h)i(J%g;mBn_H6M3m zI&X29i&{l3sQPvEW~^)nbsW-y1vUMxuvwnULhzU$?@LiA{^1i9VR>`pzK%dpQqxNm zXlr~0HJ|FMPVFwA3%3lx_`aFN`VU$l*Bktj_EAa3)pCj>RuuiJzNQ zM7EsvJfH?A&uIz33n53=QX29vftBIh)VEGw0ui<$wRO?(R|NAz4eFG#!&J zG1H@fyZ7igka4H&myzlTUXpW@DX5|475dc!LGp1@T`)!+Vt;p%4zdXb(Vuh2jvlq**`}34 za&haiXrS~b?JSvziIsg*=LXAp6rwMUF=U-!H{ ztnYs~V*K~bc>=k2RG|@kVkUab(z2(m?P6i)6=&z?$1Zt$m$^JWd1SLjey8Hsj=X1k zmyU%SXyDVfQihA@8nG9wwMu&n`}>EQnqA`J>wR&|A04&5V?XvHIr$tw6oB}wlIK1E zGN}b&Djj-oz74*=9*;hT;@Zo)YHC#x#13s3?;Dz$A|LFVRN1&ZXHFf)bMrEf5;n!axo4m6$R~=ao~XqFu~^J8-#!8=jNwY zZAM^I8X!x^Egb=A>K~jlv9S2IJFB!I8Y#=4vJxq z^}Dz%2l=Gqgwl+M{I%|^cL6UUIfuKrb4+i3Zkn_DsHrI&to!I2(M5Pfk305;@wrs#1egP#n`I|)g}y|Tkhy*F zKn<}ws|x{Tq;FDT<`aT{mUz{bc9iD34mEukZSh$}_#S=n5l z=&91*fC~%EM5zzK@_!z{;`y1qc_Ax-8E1&E%}Y+GCt zhEwqoo;IzSRc&H)uiC?ijPPthj0^45G+94hL}AhTiHM1X968Wwx8)zd<9#Azi`d=C z2^g#2(CtA)q8|b=*+alDT^)!r)yUB;SHpyXdU8(M4q^Hj(<+I>5OH$U`^cZTM$5&u z&71@TYHN>2i=k872z`ltvje9%lkF2?sPSD~%rj-Sx8yb}R5LpZU?03E{LiCZVJ;vE z0IUB(d>sNEkG(hqe#ifto*cGE$IiF1bPXo zQL6th<0IxuLm=$A^_15b2oypIf%9lDwuV#-Z?Om?P1n$+ii#0Pc2no!;|`rbOH8b# zhDXuGX;0C2D#~zUTIj5b4GD#d9b^a~+Yui|o*OV*7!9`2IAo_LC7pqM8Or#p$Pqk| z^(=&^@9RWfRK3!OdHiB)z!b)vqxDcHk8zb#7fGSZKg(Z03ZTJY0YDpXI{AMp}P15 zgrPz&+YjpLFPsUK@0eAH&z#4PECh2@v6d0B9zPt=|CghF2GcUeg{=jt9V^rK%dbK9 z2vZYsL&9Xffjgp1qQ4!QXWX}n0lJ=r-m1vDA>#;a1`i*AD@G`+4fxlv;Vuo@UWGSEMOeu+Q)jNS<>C`g_u5F)3Ia z8Eq5NttZb)#?C`FO^QGq}SI$*h?s&C#J#G35Q+NC6Ed357m6Z}6?8 z5dqQRo|p%a4}(K1_|24TTnbLq^rTZ2$Tw$a=WXIAWBQ8~k`w8L?u|6D6MGsX>t{`M z`+fP32_Fo3`q@COc8#RdwyikV)DF+Sm!MZO)GQh-3ptbBwnbmT3BDG1o)>pT*+P_j zzj*>)tapkvlPPy;ec+`7GIuNIu6xG(Xy`oz{p@T~Q|5 zqLVMS(F|Sn!HpzvpG8DPrQ+)t6((`SC0##XFFt_J`>3hK9C_!VW(I9WMjB)l^#+}t zuOYR{AO|cwa(>kZl+EqXlS_bi$Ns%^K3Y(t+@8NyS=6px4`OUp~gj^q!9N{m4F6?Uf}FQYebV^jNn=T!xvZgX|BkvFYl?J0SKf& zebt(!;S_J!9r4M`S*aw&d;K@s{-Uf@W=rUX_(de>>Arm*aFLdw)mK`MU(nQt0=obr zbucsM;aLEn4U3e>BpQT9&dfhK;76=3yBBuiMHipaO$Y6M zLGgtn(B>zCX<(iAf%NydXf!5xo>4yr$P5Svy#)EAELDhwMPo9_uZwL-Tq1@>99-T9 zk9TI+zKC+~sJQ@JA{>#u_bbomj`ft6N9770@kY`qMZ;$%QzQsE5KlUYZdDbw26)d_ zH5?3e88>UV_RC()z~Q2weEKsAhnO^rewBqJA9?)5+zHI`n#YS`JP_G7*}9%RD?rK1 z_8ECea?#!A;lUTSL(f%sFD~&i{RaPw4rCW);wOrS9+FgdL8txM;kTN0+DNeWi}y1gd9s;;z2nOnZ^TVeBBP^aEHxWlpup&4 z6nD)gGjglDI=+0FB%Leda!!ybAFzb=arNi7p1R~yfo%l4^jU>^?2PyAvXaL-%#L2c zFN|u6b}M!FR3I}{C9P{fYa>kO3mBqPSL&p(T~0n_Nf1RHmuTRUTD+l?|8aR_{|%kr zp_;a4y7~;k|FEBXhxTpa6ypv>F@zNr3jwVk^v@OhhaLdZf|wx}ZYJz!r!tR`T~RS? zz*%3B<0RL1%z~7RK~yS!mEXbdZBYO7i+N6)&d4wP{A=i4;faHGhRnP2P*8*+*53+& zn? zr$ywpO#vzVDQ|GZn~m%%f`;esa)FsZ^Mi{zwkf+le$909=BRklTS( z3PS+tk!~(aCxaOeb*#qFXbQnbopU)gr7%WvPwK9Qb*~w0mewenUC}$%_mje3u|bHq zdIY+6!6%*FFloO=BmwBRV!Aby)2xFSFk957NHSC2Wf)W21?vft*-paE6EC}miS`ph zMn_y^WMstAvhmB~L(rv%R-M5}qoZJsA7-iYP*}lu+VN1UE(i*URV3wIQ|!UUz#UwL zB|{QB!1c8R4N8gSoLcc24+X?l4}rEz+4dSSP6@oX@o*t&2=(KYQu1pYVNr7eg^lPU zXZeI|GcpGcn=f|WSJvjj~5reFY?`Q}_|6(2c@%=A8|kr9MW+}r<)jH%6NZfQE#dorIdb$sx}QQ&Av_9n0Nn;bhPG#EH6h3Uq2fq3v^W)X?K5egG;_ma*=WZ@U|OiUm| z8@c`EP>(JFM_8R2W)1CiA$%cdZ?CCtybdulkDAtYEOA#T^?(hH&VOaX_f__C z+a$QxmX;P|&uZ@}BV%nFd~aNoB8@3131QVzKyxHKa~K%Sp_#ZJAAbN<**gwV5fKk~ z?1XjWny%Gho5toVK4ODi3Oy=9O-Wf9l9syD7Kry84rp@!9sB8A{U}O>mCl}(mZY7v z_Jt(j4srVwA94g`oWh_W@PaQXu#y1^6YhQIbvTHcw72$!7cN|AZD|oA9$(t^vZ%Z6 zh1W_&dk@tiX5QH@*&@EBO@(9YNxwm&F^Cm7Z#!QrrOHw$33{exW~_m##N^;C+fSH3 zY1iLCNtQVqY27|LIvT+3c@a0_>An}YlG3}nPTr_AK+zrhm4PDlqs&5Ehj~^?S zXnT|94*vatHzSU^8}n#7tXWQRA-d{R=RcTlRiWLnW&S|{Fa$Wfp-2^BAqSKE^S zoPM@lPqz+O>Y*)nQ zLrjSGu+7JA8Ep-W!T&GOdPHW(Ib-SDjf1O29|NBF1XeB#OdM1UFWd#Gx;4x}lAf;% zk3r*w=mwD9`6uv0@CUR4!Na#yobUU~?*kfuBmYKA`LX@5oBGHc7!m+(R3bK9?(P*bFX+JHK1v#$-cd@@!V*uVHH@f z;~@NgrnGjzx%edU4<*pDIAL^$j+zQI#@6vh--bC{-{Sy`kkY~Egr=4zF#4=uGhyf7$mp(GpT&MJ~C#4^qZW^ zhYzpSZ3Nnr)`+8hz}wBC)i8RZcxiFW`zI4EC|cNU zE1(yfal3iP!}pPk@a*LN{3lxnVmg+G1v9i7!fJ~0ldr>si@eRtU22AaxcI{-H_M%q zeiA0Qvr3u-$8>rZDu2#tEpp#mwh1y8id&Gj?8`nGZ@S1^C;c9j^fR(ds+euDM^Xhm zCegTv+56_W+%Y%G?dm?{ept5yfeBPi`kMZ{D#`7LA)vel&P0=48~nn2hIMrj@J)y? z2cAB2NnOD%dF4U(HSF6R7hN-L9lLgrP)Z~x1j8^u;-R0)+D?I^g7wJ1B!`hdE)Z2+ zw?4k}y{4nD6?=G2oNDGVQ0YNQq`D_t^ykc?Su_r3zxR#FDOek2@2G3Nom^ubf6n1r z?nlLtw8>LDvG3A166OJYR~4CP1;^W!|0B}`>VB=GAk-`ZHj#VWPEr}afZ74ZiPG|n zlv)jk{+gCLS(`7LP_XHZ4F6FkcC6(27P}A!0D{uz>%vkn9ke#-ID8bM^>f*A@(hET zVc@yPCcAWn?ln!U2S^FkIjLzUFY?vLlEN;>87Wk z^>Yi&{*uqz#t7;|oZfB`f+>2Ml(4w1De5+5voU78nh_uyo~WMC;}mq7AKZVmgfmm^ z(heE@)C1uLO#JSjbU#1u)OpA-?{E6^%7m^n`RfRBgl#}mmi5WU<~8#SH8o#cWhVK> zAXORLhYup=k5DLaKE5&Ljwg2potdHDc}x3l--&m1@1C%COPmub8D#KkkE-V=d&Eg7 z&Me39FR+ZN#wrV5r0~8g(#X?spnT6hb-}6ZzC~ej&Tw(^eXI*yn;JQ6GX~dW9Bv-? zboNa-i^-?mV}A-XMCAXth;V6^v)*J{tL1tpQ@Lg6)4?adWGqg587Rq(G-C~|*0K*} zRk7m*y_Iyqtj4RJ$c&!bZeX_ zDhgj^%_=-%>t|cLM%S_MXz2ZKfE$c5YowM;^5_hk#$So;%75JCd@gUqDa%Vcz0TX0 zvNvngYQ$(iSSM>q!GwrK%4a)Up^u8jSm{$qXDGw;7*5tRMRxbdxKdCeC( zaSKN2)$0kl@{fhE3Nhm`hA>H-X8S^a?ZD=Do65iK(z0<<`Xcopx6S)E ze2L9{e3Ac}5*vR$`RUAdRL#WGfog-xiKzz6V2V zEH-MDq|>RcF-iM+Fu+ISeeYQ;~Y+n_@-fet^9=YsDXf2qsP zT-A8W_q@(-LF$u}oRl>^J=DB!*zR@o#X#DWjZxsz;>s@N&DhgJE8ymvnAX+&_+0o4 z_on`W#vimrr#Db47L0v)6NmlX9-G!BM1MK)A~Pr z>8T+>ZA5Ua>Syy~%(4aU}i5_LFpnm2Umn^CLdIVo`on zl$Oth9k=Ln?}Gq72bRk`p~k=6d@ftipyL1Vn%>{xR>j225hL1Gm+$BlJRFG;MxVr>eS&l}S{Bfs6BpgF_&dYDnS$Ng6^UMvU}KH;_tqt`^%%cVn@ zC0v1-6X`#@;~KZU*{b9_#{b({B(k*oM;e~1%-dOi`R&FIMU$>_lk_I=#j!N)mE(DL zn5%nU$Z+^a1mpLv^0)ehm3PgFLlsx$VIy19$T8~jIffSFgR!x)P=DOWfaW$b#?tRa zbWNB2Yh+v)t zo_i6*sZb**4>Y49Q! Tyj7eH|7ERe+v!JG#vhKsh1$g{q~@5om|{qvhBO4{f}+ zP!L{C9_xU_Eg~U8zu=+qJksb<4qgrQHQJae+r(z^l3Hh|qQI$QaX@@szO0SO#>T0G zX>|i3e!E}aO5cKe_k!o1=cwLLh-|kbGO7X&rx*pQPbyd%8tz3p#%YP)I7}0!$BrQv z2u^ZCmK&Adffzz836vP8^r^uBvg<-U=m!!khm&PLx{KJ^jiIa7DqsX8dsmORP*nh$ z2@Vd%uUiIe0El=3{msj9=uK&TajL9D&5HVvPK-q2oGxV&uroT#>_&3!-ReFuw*yuO z(+KpQ5<8yLA8FYwuCBaOT{Yc!V`~u4V4!6v)2*Vel*evI3ye-?GOau1Z>;{Ux79Q# zBdT{LP1P_gkz$BYP{0tVBb^h<$`HkwcGdG9I>=7}){l;c2V+-?>_=~ELD-helPf?AG>|HB0!zOCceb-XJw0<6162`^(mrho(x|iyZMfFF2tDE1DGOh z+jAZY4RL6^)Y>;i2F~DAC*}rh$_)wU-|R_V=gbbx2z2(@B`QiyNo!pEd|={%*Lbni zay3H$0Fn$NiJe!(WI*SR24Hgb5Y{Pr6_0iu_xWKTj=vIIrC#L3yQe{;-`u&aWyNto+>st`ML!GoA7)L)ThM8& zP7Nw#bd*-|}g&e zBqZ%)^Y=QMPMVKy{)fB)sGULHmW%v1_?|%V6oLr*U#trvqhu*v7%hM@K~t|T5UU6) z09bm06RaO{VOD|dBAN=W88S45`jE%y(=^d-wMr{fL@1&<(VD>t020}e8H9>!s|E5P z^!|Q*ECr#ntJ`)3#%Q|JwHI&8e6-kT%NV}t)yEM{L)P!T?x!n$I(o2OE@Al#!<@*n zl!I4BW+s=MWvI*gxVXAD$2Wu;rAJrQt3Iy(@uB-qmBTq{PI1dl~gMye7;Q2rd7e|U|(Qy7>E z5V@zyJT;#drl-?V(5~a`wPmQ;Td<_UX>s1#I)8XfErA|uWqbHW*1z1h6|Q-$y)9OK zJ8^c)$Zrm@Jd4&5!f`;K*Lm`|rQD>{?b5DwDH#Y-BAFwz*o1^Sr>^qVWd}-I>D63t zAeToQQs^k~c?PSWr`UW0sy6sJ!Ha~F%QRW4kaz><(tNT^{+DP^#K}^4IX4Nn^bZb7 z(i@wb(@_N5QGnDFS>IEZF*o zs{%X=_yTZdXoY~MDy%S#mIGv%&G}EPBR0WiGl=^mA@%h)ehhCX3UZ#_DrmZQ|2_?c zX4Bsh*_){&P_%F1RyxGaoPEA7`+`8$ix)nmKM1}wbU=b1EoUr1&4#9tqlSL)wd0RV zj^B5Rcx#UveX;pY#l)C;%yTWp&p&rOpnRMvJfQw_!18Lvks{;yFNYbW9TFl$k%14h z6uG~^<+hGE6n0E&rl;Gfn#+f%wc%QOy@Cu>6C;6AOy+wX^>TO7L!_2$xHRz>ZBr}& zOIC}fM(L_!`@ncdS33_F+_pSX{HGQ!Z`uc85k;`~>8wo3&H-9KUxCD#(wWa-t3j|; zD4}^_M+dArg$Rwp^IJnGOR$y>M4Q9dk%GBb7qho7T%CInT|H8sNGnl|3PwmPP$jaw zyma^XixV!(g6>?yCX0+gyAQdn^1E|Y^EvhsJljn(-t7@*E?bfx7ECz($p#)yJv~53 z`T6+`><@=vF9!Ph0;KirBr`)pD=Bt(EYJAz)#lt-&p=R-D=Dzx{jBssNx$*E#w^V+ zMv$}&G%eeLA#EUGX~++}L|B;BoB&~}uQo5i4zwjoN&rdykWD06S*TeO3pz^1ULLb68>=_WC@ZY(HqS4}Z$4pnDL|~C)vj{G2c|CC^j{&r zxHNo~HGGKV!4p4~86Hr-o-nn19pO0rfbsOe+bf^1-ZklHZ%2X}C+T|&*Gj>^YnZld znE|Z{%(N$W{6#8}dLE6fP*gwd*x~b;6v=_XV3NlHXR=y_c~(mej$v_rS2(J zzN}=J#*UmTd^u;`a^~4npM(h;aqS7d`MfM{gp;w^#n`Pwdp^EE)ceMB?c~`X&1Gd| z0wZIr0iqePHx7;a?2wZ)TX~PTATWs^EM45(qKR@3^vOV#)m~Y7{p}AVLZRgxfdTdH z+c(Kr(!4?NSnBIfjH?|4b@vK;U6<_84aGdfb-kj|(O#;ZU}pR%;6D6?Z_#!q1o=qc zjtrPuSTLpk4UY0IkJ70r7dWdJ7#PqNuXknHpYyRa4Ve0*xkRWR3~l+j*DH z^ypD^P64m5>&WfEz{Mq6+n|nkD(xf2PtPfjTz)s=^dS zr8LT1@7XMs^5c(fD)A(&y7d%;1>Mmsdy3g*Iq~;*%wNX%1@GZ^61rKXbAkWnEYGgh zP+mT*YK~ihN&*hsr``8*-BL)s%RX!-!If0)BD&X!C-r?|K9jJ2UrTjZQ0!+_+7ZU| zvhXz@=l9?FL)*G?z2=^dI$!AFlZ>AQj+L42-?k>^;q|YNcrVeduo6MN-jswa722!2 znz}zIOZQr|)vMJg&`QSMRq@n0$#|Eok&m0lobIy&zbM_{O3^Iy=Rc2Em>Xkh`E2iW zaIn2JV_iLXr)yZ~#HgxnXZ(YQVnWM)-*NMBwCCYqM z(B7c0xbPJNx6DJEnojcI#ITnir#bm6UFy)&FKo2c-!f3 zTw!j98pB37wNh*-g>$z>Zv2$*zg*38I4pnNw=tb+e4CW5!%Ygs-=X6B_Hap_y26;d zD%{G7ds3RqJK`Ti^L4cdDDd34|9vpvHoJdm*3-9}Rk&|N%D53uMHJtdGuF4kw2|i!7`;VWAEM8q+ zb{-$IFFD4#wDsZ6sxJc{R#>VphyM}k*d2RUjnVkN$&*vXf1*^{K@>050+C$tc(PMe3V;`VOp1u zZd>c?zo~KmpRnl7UEP)YQdkQ)n)lvzR_WDQZV4u@O#%;}kJu-|$=w;YvPp{{!)2WR zClw`n8{WkkzqY!Hw)T3@UKjVa!Sc&4R+1ZEr_{33vb{_%+w80)zDhH6w5XKDb*B!Y z&gZW2isE~4vLl`|m?1tkhS|8++3bwe`lk-PMQa;+4w!^mtFN(|`q71=V!{y+ z`q5DCXGn1HkL3gTG(y>Vyg&W#$agKXhWfY4vd*ll*~Gh`{qDE#Px_@|$CFfR3H8*s zPq+Nwf@Bg==hUm_V#im4{$_(cLyYm?y$Bhd@|ae_^2I<;@8aspc3W>fGe(8N#@4Mq z>Q3UUY!hc@g>}*}yW}XmW1MEe{tDX|xa7qB`_4$ECSfX!z01^nNq@7Cx$U#xd*2r< z=n@S1L|$GVR$;h}h!-zjpnE++!v0y0UZzdye!lC;lu5JEDiGCtgI@B zX6lfzTvvw(ef)7E8&ymUst_YL_4T~Crdi)&C83quk?Xz`R(*-WHKn(8Q1S~O?wz_B zDE$8NVKu2BpEp%Iib|O0>)%>!{@uOoGo3>e7w?zF*X_>uui*`P-areJl%&ERPCwXd)XL_(`p6ICP^*V6rlwKGoFKjk>R&f_xpDVv_)gvcm9J{ z1aafW4P-?7zi&LXl!T_UPiD${_htCx;i_k4Uq9_U~)u-#Y~dT;+&oa<44Dl9bJlk6@sjx=jA_D7!zwQlXB?Om$PN?- zt04tE^Nv>fPjcjooE#&hp~yEpebCfR&|Vd75Es-5;H9g)|M)_!OWc~&oomyMoINj2 zAApTcQ@^>Jh5g0jS@%dTW%G3&Pfjn1UVgOBAvr>F*CSQ_j~JmLA-Z~cio{B(GVgci zVn7A6rElx%GFKCh97E^uh=>TBC?L>#_)r{Cz6=a74D+zE4v&o;M8B--gp8Q_nC^8f z6roQu*6mdc)7&ez?!*y{UX6|#r%&4&#VaoJHyXwyKbRL1zLzwvpvCz3FKfqcE4kTv z_6wuy6!f;(X5DIW3Yq+QQ z<=geu(KoB1YkayBB|GE&bb|bAi@d1|LzT^_G5S9H)#=16%?&&JPT9c}efmwP1ncui zx%+#_91JrP6O;4jQ^Ugz9UUDnUQ919S6Xh1zW4DQfdRTqtPxuH7Q((`Q1^{$>Z-gmwQR_@uGa6_}Kp?ka~@@}WP zfe(G1Y+}`|W+}6P%JcGajEZ|#)1^By5-Tf*>sSj$1G8+z5Y>$83rU~>IC!h86V;He zAdv}S>vnd3xJIsbbas+?^GQjYAhj_x{4qFK;4#&UG*+_{CnULa7t_mj8h8oxYc{_V zP-M~5-s>jZXt@4P-#lx<#ut}jKUXH7a&Xv~#BB0FS&hY(nfIsPh+NyJn@4GN6jr7= zg43z1C;m!VXIuRW)Kt2$#JZ1A=HlWC4-bbhsSNGkQc?zRtb|5U?);rN3++m|rzUYR zF{H?sTk)EiATxxSXgjPRD*9Z>VnBIJH*xiv@rIF0sHlGLn=U^zpQfYECXuJh_u_*5 zJKw|!U#l#E?^5SwD~6cv_zSqx=y~BwX|BgS+>}ixv;* zx3~eYUWAI}*Vq`cc(ImwAZm7cdK!S=gJ+#vke!ZRnYh%XjNyvpb{Lc8Ll&Q+Nh_l>{|f6E?c^M@UwmC z+Sl{8yeGze>FbV%xh`8DE?7_RZ`H6zVybjP8SZ&XRlt0VxVUgaa+KQ{jC2~c4POQmNWpYRA z6^X#F^KRP%Y4bOvE8KWfHf;Z@o>+fbS0lyIJiw4=rMN+oqRh)g=1M@mc};N@=M;+~ zN4{@c^Gu~>WDtw*h4TQWGN~W~CM7+0Ff4=_%tm-G4EonFSig03;Y1#@N$ugbd|8!;+;n~O6}wmie_Z@%C9hL{{yVkPLHVdF zyPcA#rKduF@8|#N|C08Ca8QILu~HmMw(>^g4gOl6AHTAYkgDOZPe!I24Git=$8kI) zZut4(7B;uc`*!Fyt-%*WUQfBM8UDSb_asw?{l0|MRY^`N${YUFD`5w0w^aI696Wsd z^2_~SzN}9U)10eyvzt=7=Xt&wmkQ^>ADJDC=<@arM_P2EzyINb1|wrA(ri<=gbS;V zg)r^-`sG&7-iz!gh7xpj(niu|39gC;)k}(v`#*3UbqeeDE~BFBc>jIVjR6)pQQw(e zMGku5ueX&Rz2e-F7i!CK?!Ay$b9c+>%CsQ`GZ-i32o5SwL^0;jP~;PvFK z8yfi%pD>sCW-%_GA+D}Rl`W=YUxFDlt6R%(=7ahQeF3}+j1eddSOqP|tkI4D2?O2*mPw+h zcnxsrh}3d;;CA*Gl3adhEaD>Zm#o-Y_MMhoU(#~grh9n`3M6!l zng=r3Q+k!iE$LBF*H+0S98AK-}hen-M7uHv3DDIVm%Y5*v}c^k*R;EuP4>+Xf{sv zoZXu+V3Ppw7}vk2bW-K`@pc0D5k(5NNB19o&M)>VIrYASP&!hwsjuP77xZbqUs&j_ zs;UYxiU8Y(dx`T$G%&zJHN!=^g5!BG)O+^MshI9QF%$Fo5cd(Gdt#?bQ8jn8> z=F4cA$IE*f*;6OQTi?Aq2f;Ih*)3l@DhAj?>!Is9P8>cc&cF}?F?>F@e5}mpcdflj zg_}CUb%S5RUe?~{X4fA4rt--n*Xt5*r0o)ym;Zwp5kLtL>x6dydAfDmHhbKPj*gO| zqTdjbz0A!;AJtAYdh$gkJv`>0zkG4Tlnxq0`wZ-c9tz!HImp)Du&OK)KhfcIZgy5m zN@{j`y5!no&Y2~Yq^LXw`u{o{e2iWOOMm|CddMy3Sn=&!!rLG895TNUF-C!ojudy( z(FNn_5Gyl}P%2`ipc}49=>TS&qN1XwrzbZzx0)JJeCN-^69gY+T9$V=G$5P}bVqO8?jyy7y7LYY_=Cvd4g1U)Xy?rjX9hVENg645X#uAcVGP1L$ z9+s_6zGXzwi`FJc*+7qz%Q6R~rJJj()fM+Igc5(rNP6Tl;1vqoSs5AHLiZpR)>K!& z0O%Pt9NK$??Kyt(gDy};SSwhDKkVcyyWAAucpPUm!_9RB zHmEo9EJ>2IIhaA?@UfMoq*(WV2=^{FUVM5s=zs@1D=Wp|L7WhQqL|JXV>2r1sIqbe zCMiH>EZx>NHXD@n_4Rp~fM?b8oI%YW7x#>H6~f`2A|hsm_6a&doh7arsEYqe45JJN z9EfTe4a$P6atet;8eYqFrrerN+8a4ckPHVsa`qf2e zsX$?swv8<9W6oE`0kwN5d+QC$H;Y3CuKpt;%cyi5bquctjd1p_AdHLR?Y;cEYiIm2 ztI}tfL@N5GWE{GG4G;ewe>DM-69p|7P%aagX-KOsa~{w#GBQG^>~9l5$6w|<^2CR=i@$#(5RhCYaS8=K zOdz*wQ$^GJixnRopWQtY(jg zyzc8$M@L01FCW&Ih|2+4P@A@J`vl7A|5Ms~hg1Fdf8)mpMJ3A27AnaKA?hTA6e)_b zM^cC)>!lEt*(7_XK|)cY%%*izLPp3)M9SuVy!8Iw*Zs%+yRP4L-RBP=e)*q zJjU~>n{1U2s7yLbScEOY^L}8qgTR&6-^75ky()9bYG8HD%*^&%LO<;uPftTc9?u>h zEc8v%6X&3yLIhl%9~oI^?)u_pd^~PWpRsRyC}e5v{!PomB0*Pt)0QpsQ1ABEs?u42 z`AphhTUp5vc=_@hRs3wed9*qxt~X;BS+$`=ZL8>7bRav?Z-{m zg3cC{Z~dA~b;bOq3fSU&PPnCclCgNR%a$p@OJ5kPLQ^)x4=Os>ZCXot8zr9@)NYB~ zfGwmuer8PK_N%F_WeAIkx~-SVak&>|JcWaka|5{@^lC)Jvgqh&Nb~5h@8|@qRHR3f z{k0wOI53bxL8RGlU_j1z{A%s$D#Xdjfb~%5=YF>2qn)8jb72jHKe9+RlhJbU*#RU-^fv&Q$vf9S|%o+I+i-FdUA8$rSQ@W5Q5tAXNu7CgD0%;R_tgl|V zf~~+Stnev7t@jcBI#?WrjXVqu`cLhgoSecUBLgsZ(8t!TY4gm`I14q5T-5bU>v4g` z(={S9nIirco4DW&YoR~R|gYND}RoCjDel~e|n zdHkJGA4ssqj*c>rb|7nBVIC`UsjEc@q-QdJIA&f^b1dtybY>L(k$ZazQGnn z?3Hz!j|WWKS8qnTB}elsI2tbIp2jCi!Wl#zk~i})qWz)fVy3v(S@)^fGhD~039MR< z3bYf=ocf$ZB1gGiq#S|($=m) zP=>?b@e~ z5_cZ>#0GKkwOm|2zP_-KYJNCCKyUDXIUZ!@LG8rt9^b(Z>*?uHckiC|D|`NY7PDBFbge&CeD=q!7(DbIH2xs-MQ>L&$sMF`i7VLk_!_5JR`AuP9#7Z^dSw7pvFIslQThCSFvJw&! z1n!ow@bEd*C_0hr&v(6~Qyr#Fs;a4x_J=iM+E^zdAubLcW}mycS=u?W$fq}`vC?DX zlj)f&P-K{FvH0BmS5C3hMwR(|Fc>}1vb44~-;UDrSkZbrFHe6a&_H4qheyuC60Svz zbF;Ivo0^%C7r3In&Y5rxzRLs-K;y!*wc;Pr=qJ5hlIdJk9zT&y3{t;SRP+anTCjxX zw6n9S{o5PhX0&y5CckXqWfIOe0|%$+>+KC^(aX=DmA7tn1=~tk-iW;`fP&>NTft=( zl?WWQk*(d<`qe20J!1Fom08!L1>L>8G|vv1`1zFr|2&5+sHlA5k!iL$qO#4{)b!~I zngla*D8@BL@W_c`dnz98$3;co;#M?vGSU4H`O@l}Khj0*ev!PQJ-vvl>jrbeVxwhL z@~Ymy=bj=hH8-2=DUZ3mefRF&aGPWB0bY)d{SXd(X=}SbHSPcf1fXQdVh{`Q0{&it z5D4?h2Zz=CN(RRbNVHa6z1TvAX3eYp?<)?a{8i`3s`ODU70Y?K=P$)4oESk^9(YYNJX=1vnxwzQu_)Tce`a*J0w%`9joE(b2F1#Hva{}0X$o4LC^w2H;d zFr=P1A=0VC{<)`DDwf!Haa1HLU5dBpY4+!q7BeJlTrtdHz)1l;4GQBQe?bnow?29D z{?evgoHv`XWMz$#+5Ds=7@_dLON0?!RILKmWy;DnARfY)h;LL_*!z2$WKouD-ZAv$ zLkC2L@WXt$q9SWcV}3;uL!I~UY~1znh`7mLy6tmCm9 zLEjCkLeunLdh&gA*tYT?SsG@eie)2GoPeyFBl4LqUK8H5t9@`R)jNMZ(k-lmw^~JS8#e1iQKg;r7t#Ttgn`H$;*r_*y0HX};?WEh) z|H?+Jh8cht?XK(-F27Y5A4UYr>bAxj?q(6uL)*h%lzB2Fr$427j|JuEZ@gQDO4hGM zh6;KMpXHBFXVKWHuW4z2wZl|ta<$Ej1Fga}w+(;#@Z_@kMWu+Y;I_=5*I_a8B4Hzs zR&@j7GMy`>kQoRL@^)3$vZeBH7bEk*8`w@lW?8vq7zx4Ewe*gh}gL zyMAd#j`tMLtpLJ9z8fdrO}9KHhk3E*(W+fGJooD46SYH*FufX8(Oc9f4^)N&HCa9? z0w*|}r1Q<7U8BO2ZynBLStwyN&YvrtPJSjGveMTv;{!-`Nk(V^+uz2o_yf**~r5Y zW<0-n+ZTiG=ZVb=paYXFxEysUM?Z^waC zziGFm!E?$n{)EKfRxR>t>PF!*W-RiRGM0rJ&8XGY@A6{CDO*>&md?>ij(1E%YX0BH zb0XjOGgF8y%8o=GsuZ~FY@=W*c zw52=aN_9=_R@p)BwSJ)m*M+4WRM|y>)!A{EF;UXHSq!LN%r1b>M z9*hzdgy;Hi^as|U!d{n^8szRPYrSY0fy)^3hI1m7_esmQQd&dXcDI zv@&2hO(wm7S5imRmHF$ztL$m2igBxUvJ9F_uk`k3$YsqfyKu`Qe41W*X3Gejw;21i zD+%@P{#*0{YJ*>NJ(EqmJfVu|)9fsWMA-kn3xQr=|LWWykdTp)nx-9%pFgAPzNLu= z7@0sxUj7@l;0i{?HP{6L{(BwRJ10>X;eLK&>t?wzZl?iR0o_CSMO{w>KhfL=?Gwgd zzFyt+j7xfDmTgRSQ+s;?2G^pF2F8RI_xN#{#R9OO!t8}lo@jjNQWqrZU%!40Ru@Go zUg&WavLV|6R*{sH^jL#cK04n{o|neUBEpsvSvlPg6tzqzWuFJr>J8VSA31&c;$`L< zf94LBtc$(>qj|}qzsntGaM;W&7RMmR_$Fg(G5NBz+`4!dU1dqhG{zbuDY&?}5C#SY z=-u(^UHKk(^}&NQM8Pt-&`KlEyicDFU)G{eLG%SJO`7*E-nzwwzBMlpVHyPu4vwFA zA&HDDx3;vXd?kruFRw{h_y0_r?l}gd?G>m;b#$_VWS4FZ^MgzrSwXh+i%tV=3W?f% z#BzC@O1|7zCwR5}8CNl^yVBZL2^A1B*^L`FkdGbh?AjqeEGjy*d$%owt>{q%lw4xd z)R8>F@BZ0$4^1&vu5nH-yvSTL;{RyBvn#GCz|lEi&@vXqFC7Z7 zxEg&JUOM4c!**JkKSoB%U&iJGws2r2fq!bJ4izAx9`q zyu~1GTCahYR_CmAlX8mOIwb|!@!hfEgPHSeFaKg%R1x%`8jX$%a2oEv46 z0ntBn5+xbXQn(HD0VSoU_dsrW3q$-<79{?=aMSVM`5~ryvPwdy(oG8Q62{h7>;8MU zqW#OiN5cJtN@MgoYXhBKaX*si7>e1t{@<8{@VE739%?E%fh*@x=Bas&04M-yex%T; z5%A^kPv)v+L=R3GQ+w%Vk={n{bap@9R&5mpx{vv|EybiEj7mL$jy;VdVSNaXkck%E zg#NfK<_h0YaE#2(c06%n5bYWIWsRvWhI^;a0P9EJ14yy<>ym{bt4*h)8E|&9B{mR`!5s!nyR_@eUiR;1snK#K& zw3H|JWs$wY2bXagh`@zy&nZ$Dd#yi|9RY5keeA|ZLe!-(@P-`S{2eN`r7Z#BnRw`^X znZO_6JeD^IEfg!K@{77U@p;ZDcw@6-Bg=qT}H_9W190h-0+@RedEUK$* zy(}z)D*dXapn&phmKVs~j}uNn7n&3g%*e&%0Xh+y`^<7|wgh5`?pP5UO}B218dTAm zCSm(txpjV_C+}NrUv^zI_0)UMI{~Tpm#s6BQVYAM5Pi1NX49~PZG%Erd;c#dLiLOf z*49E-0aOHSZ%EylgQ_rZV_5{)bS#HOnVr=mMJKSGIFzzz&LR@^X7CW|on1hv$NXm>l6J(O z<;DcK`}eWDho6V17!v}XJQ3W&UI57P`}gn1^_;A%puAqbdv_(&3=IbRG}8kXD+z3H zbd+ZjF|}40(u2#c3{1+v9l}0uzZ|S+}}I=7-MuNz-mu8 z>jGCp;_vMxdYAT`duh$^fJP5i-*`k>zGKg)!zwSXaJRkADLY6Z zbp{tP_#BDMeoM*Yi=%cMS@9S4G12q6VpqqmAF~wOJbmob(JIdxoq^$DMFoXf>}~Mz z)%ky-AFmPr7y5DFAN>gVHK1;AGDPPeK)w`jZ|~Qk(BC~kQSj~^zoJt^@N&-HHb3Co z6kN`=zU8A1dih{3;Aq+CzEc~LqqXaDW-Ld`c6qo4M`zdYU+CH_D;U)B=Ji7O>9-x+ zY@yb#31fqqW%V~iN?DFSe|1*-th9`Dl~9m-Y*g@I@tDS@w8q07*{@EYqU!&xRDSdR z{e!%`06er+_ zIz=lUm5P^Zf^Jg+3t2xXAGjS}W74h>Y#vYHEey9BU=$;iW4$Y!RKmZ$>Yh9t``|f?%i?BTLSD zdJ-eEw**iBwZmI%;Mfc31)4sN&nP@ z$vJ7QE#aVS<*eSWdFis`QC7E}9SN)z)*kDh)|TwFICdeS-e^t8t@XM$JrmZK;h zuguI;d0%@o)2Vn;3sl-F#HX^7<_RMIljwH0E=p8w8>(zvBaFG(d{d8(9FFv^4~XCs zWMJfe;6k{rQR_EJN-_tQrXDsi!Aj^d6gZgX2`RtnQ~!Y8xPlYYq{w<58mJn?CqF;%=vH`yItzsRPyckM~W$~ zs~L0#zEC>_eV#NFgp>(P{7g-4qkVlN9(|T!>i6OGUw=xqhF*Lj>voZ5%y#~WMn6V* z0Cxil1dZ;^*+GpBLPE(<{C$1yMT!bE7pJ3gat4P!-alqx0b`&qG5}V)DJT>mg`RFL zBnD%4&tu3F1^c*{r5+gg@k0&#J4CDyWdKO+^qR%hsX{eGiH&Ut_tqryY?{k@1;zxe zPlxSFug>Vl{t_$n_1eYQWcxsKbwO+U*}6C_w9|#()3*twafF>t?_QymxoB=4X}dx| ze;{`E=M}2o8}b7q*LhBL#qJ{7cv>YIl$frrq+BcC_Y+d=ohD)7gh)>^&)UdWTUUAU zuM*y}U6F%<U(#fW(>Dd3m=(bP5quRz?QEMr01+ zO&|B5%@sXX5YH?2`LU9%wt$pQ_g$;fK;ik z9mLrP=>i&lQzP668B=~`+1dS=b*bjsp}wb^+_;jVhMA)#6?BF5gS8oxE_AGzjVFZ?1*`224%L38}Q!l$#&Z*~>_V9=L{-6(J^_%W9g;SGl_3G6tRt)_}*l{a9{y8kfaEtvP9BAY- z#Z>fr5UxQkW^|1HdQ!^h29b>Yv5~t6&YCfaSTh-SnzYWWCA2aF^A1>!&bvb3m zY}@|S`w0^b-6O;Kv)AJ_i2&$~z~A0&w?A=n^VY3-`T6dka`6Qa+eKrf#Kd~wyfrWN zmZLTLh-29x+JRB{?OO6}^+4ie?23W5#6K%JuSb z!Gr8q5T^OHppXx{jW4WBD$wL4s^&V=E`M2hO5R-dhhJh-YKePde}oq--7n z*0yqD5k_W6!s84x0#HlfDS?(0(=`cK*)z!s2V*Thp=BsSy*tXn& zSu`9wysR-L4jXWA;-Reo((}qwvh#vN@3v&HdH#KdI649$SEO*!=r)?Vb-cQ^z;o4lhldxXAT^QMuq|f34M@( zjy^LzU|IH$DYmQ1ZFv^ATnVT;u~qT7ttz{<0Wdsg=OL)WA-=U6-4?b6UvFXPvD^P!=8Sf=R_o7uPc& zBk^QGE)#Y6GW3tBUU#b#S0@<+1T6f6rFeye@-j2mtyv@I+EEIE{zAzW{{K+0kB85( zo0{&VvGF#xyZ1`yb+EavY!dMPyrTSK(U0Wa_)H!JP(apad~WF3qBy(mfYualj@-LC@Gd}`MFElwd-V`LM{h6W5ssk8kXcWi`p`4x zBc_c%0i6WliJw5hkq7aD_Hgd)5t-X+1csHKFS#+Pzbcf*FE;}l;R-T73}wkIGc`@w zCUp9ToF79A!w+V`qNkF=F}j$b$hu+$*7)|#=itQQDF+kxg9=F)D!_PD5WKv+{QUg9 zyzV6y_^zu$QIOtGPiF}H;K0v2f4;d4==;J>aSl`ji-({Y%i@5W z2L)B)yoJIoyHM z>_?Ay6rG~y_M^X$(5zp$2ys1#m#|jn`#Xo?i~V`ddB~oV^n4$;s+pTKm|n%-e?57D zJ?+VaOP4PTh#_K4WA5v=+3)UYNp5KSCh zf6zPNG90S%R0|46B_ZJ z+RdDMa?SCuSNcG{7u={FpPRdJn}raF+jd=zN|$Q0!=yqg=`$Wy%Bk6%+U2E2%V!bE z&>_zt(4hY$wYE-y054i=ilL}CF`A2WmEWCjh^?cHqvSm5*WVfiDD%XE} z84De}!QWYfxnBhco}S}`Pqa+%R3B;HjGl!al10R8z|4wx?-+aNa*R-lo#p~hcyGTE z+B`FmjNIgx>dh-GCOVu>8jEXXs;I4z(~weQIigJ3F&UQ*Fj@QQ(%1EjmNhqcoZMsP z=ndI)>dtv0dWW;iHyGh;M~6OUtLnWT>(0tW_djmQ=1f_EtL*Y_OMfp&$es!MtnB@S ztLN{|=*v7pTF=2pRIN0S6boikAwNS#EquepJR&(PB`eBSpY{*$IR}n%=o&1ZIDrfy z5;Qs%5v`SR;nh?5;5zk0jec#h@w4t?2@P?xV5zh=2o>AW1XQ50QH1Mi8&vA#Gay1) zBH%{87!zZSzIbNyzYuYZ*7;_Ds76RN7EJp{6+rSP>-u*wiIN#{JfpCf=NbKO)1A>F zj~S$9c%Lf0s^W~iYNfea^}9j;k3~QY{3GlvjL;2GQy*dYz|x~Gwe|_R8f9hH5E6I- z5<4<-I`u#c?0}Oe$o?CqPW)4(C;S2KG`v6u0A(S6XJdm5BdYeg<~DhG`4>TVgi4t8 zCDOkHP+5$ajwn0VrA~3TYpggS`CxbVBg!X*l$`9UM8v@Vv7YDLK{s-51jNx+*ArKm zaX`dEukt;}i^u)pqAXp_%R}(s+ap%tiC-0JYh{%JwZl@x!ixKei3#Wj@KHbsVPv`= zI}{VCsqXuE03P}tAH)|qaiXNZx~|R;iU^{;xtS4?Qr*m^CE5-@L9hUY!)d{3@11{K zQgXydwQqmlp5G(Z_$&y=u#*x7qc#o>N=^;J9v&Xk$sk$=%Q~D>{<8?G$_oUaC+`BS zFXE!`GhVYa5@24r>H*bSnIGTRerm#Yvf%Gwd?CmKA+kAq_%JA{CD9{EcN!U`4rs$H ziA@%b$3Sw949cCs6&`)4zYjBIF6?ahWlBIvKdFvIgQlg`_!p>qhhz{J0azkUB2K>d z-}LrB3taHTojPq3t@L5xdvzD9FWCzZf44foSeP`Iopa}ja_WKD_;_=u>+v!0f@hPE zPR_9@_=3-*q@*-Cu^KQ9ZYX@sefw1F`!$g4O9wv@&CTsJ|9^kg7mw8O?qmWip?Coc zLH`Kk>E1co+5Zxv?kJ}+iq+FX)U)efUbGix+Vj$Z$@8I*mw1trE7(wwJ;1H-LT1G8 z=st{d)&{~m(DnTFY#g8+%qOMg%<|`(!CEJ>C}VP%I1F;?jnL*YZhTI%0#o;X1YU9O zLVCIwrhckUF&HhG5#gr3Td;hc5t6{i{OH0;J2saJD^4i)Qgk%89eVTv#8y@|`6Bt! zVs|k$HFIe$23brq!Zy%z=LA8S{umAG!uiY223m<^@1kEG`Zen|O}4hn9{m{M=_kzeTX{ z{EPVMNE9RhAF?6hhs@B*{YQ42zZOT=ss`QTF{w>^!O<%5 zh$Zk4yEKEftXA2Z`8M+kPm?4jR@ubBvCdacQ#42LKn8pFLN2$RSn$K!`}#hI3Z2}J zZ4le_^XJdLzD(oVW?k2lPEJ5-z7c?-aJyns*d`8${lX>iuloD3p0yAfwTWHV|EoBt zCf9^HKjz=Pvg?$nIF*O(QuW%{*>z7^f4Q`eo^lN}X!ycNSu*kkFr;e)JYJ@U4gn%_ zU$p-_i9_ah9wfqV^#ksjk3JR=jYze?! zAhtsl<8z|?zneeI`TDuk1<`x9M)83&o1?E}J_~BOMJGGS(8+D#$a3{pZFG5Q$irn@ z1Z!M^dcFz%+_j1H1-o_a1Q3mV+EKOf^7dEeB<1Ea#8*pRe}kDX;C+cB(~75jeP_|A zp`<1U*n@K1=wRFtBBifpC-I?~^Z4%hJ-S*A`N3bjZ!FTy{Soru;*Y*C3em{R zvoA)ijB_UNJpezb_)TFEZ6A)9gegBz-MjbH{E4@B-D~OW>Z-Z8`17l;$=d#T&0mf& zH-8W^%GM>8D}`e(t|M56E|{%57@;I?L(9lqlU}P&qiN@BUym)EcQ&9M-MV*-i$6k< z@#OyHjTG(bz}IWoo{C*GKJ3L^^5EsjV`<;`PNDq?E?j2HSDuHLSrTsN937LU-@n{G z`#tV_Qi)qP2DC{UCSWMP18!|R?S~J4YR(}J)@Jx;D04OOQz*CnygK>QCCA@o%+e1^ zi5%;zx+`FPidn-tT---s@08}ZsjtD#_?j4-GzZLYV~j5Pk^$4<8ym$!)dPjDSjR#y zfTtSac1HOxgubOf1XoWrGyl)=!)2Yj-`R=gJt|OZ2p3F5)xSed9$^e3vwGa^o1&2 zoTzzB3>s{qP-JUn_;kWft22LlDd|-kDz9EGB2- z=hO1}Q)sGTxU`n#L2FtBybE2H@8{}6x~>$8g@ezB8_E}onK;WmIZ7_7mqcU)h$bH| z95MJF%>CzG)Q0o+$KSRz@o5mXzjyi>`ZhRt9!g)7QE)cQOikg5ij_&F^}q-G^^6*tMhD9e0EAYb z3H)TWpX%fD4B0~@0~ySEwE4#2`@q8UZr}cl5knx2AAHf;bQ8VLj^PRwx--= z&#a*Qx;tGJc+|lhA@s{(V*wWiq5Ww-xc}ji0&S_a@C=~2 z!~t604vwIz`EXijeOZ+r$a}o7B?&R>&Ps_k1-0dH;H~zJN(bn)5!gAbFW@|}55_)E zUWIvZC8aCI7(&_Mwt1EJqJ0CsmzHt z=-e3kGXp9Va&*RJ%V2yR4+x-5hSc|Cv%0u~!f#Oj#C5$)rM9h4zdZlFnnDSnyfY;k z_^5!1bif|1kHz1i)n=fl2jTe9`45y1EFyrD*(w1zurOYPJu%aG8grhWm6V{?8T5RH zhk{m@jdNWD@*T?Fx`gd$rd1Oot$9@G17GxdP5tV|w}krboR`}k0_IdiV45L)gvB2Hkth0~kMm@*ojELXTT)Vz zlZvIh$1w1rr>DpE+2z4&QJM=aG}?9c$8ZrmD+#&Bb1C(WOCe3=Tg*`nQ&eWCKhfA@ ze-S)i6Z)Na|6QUnv5CQx9llXYSSfH5zzMz`G9|_=&d%NJ;-%gHRRSNOmQ2&gYkY` zBE1+KiM=M6MJN{B{_O14fQf}!Vw4k$0GI!sAI1s-&{ik}^h`OgRtHBXV?)ivPe9MF zd&cULcFaOc1Ozc(O`cVTt#ZA(cl2>fE*3^`tXX1A0l(urW6ybDAxkxASy}9Aw5G-P z-Q&4B_@N@^sER4(7nJIX+wkwz(}VP>F--D8ma-BLY3?S1qhaQZGJ9X?G29}jk=*(5 zSL|OlLK}b1b{pN-{EW7_Ye?3Yxe%QpW^e&5nJBqRgOv)lx;f571>--a^~#W zpQEFnoy+}$@06EA#vT#!0*dokrL;z;2hy$&q>nt-%BCGMHTQF4VW+xv@>5=>XFPrS z8FgOAA-`-4Phkf@dWv|G@K-R-aAF-sk_-*O%f=!yq4dBv?L$+5#9@E`9rM_z4sF6i ze}8|#xZ6DPvR6Q7fR!h|5dui*#?glh)msA^8yc*B?utA;bzv^$p4rj|Yy}#3K&t(b zb&zq+1Ly*`ZnZ3SOZ`)AZrNc52aMO5GtY246A_c|_KTo--Vxy>}*D z{~&;};Ao_hJ2Fs2p*SoobDw$r8yS9$=1pC3j8!97DZyWcU$js5))NZ~v#m7RZ)A#hY#D60b+t%wKXPELP_Zy!ZJEuK-cdGNF_?n^g$6alI>)E0_H6Q0|myKyA2HU!W2mbg_Sp?|xo`=8@33~&e?oy9_!#D%t6a0I}j~iyo z9LzDnFpE~?SUB3S>TVjFSQ&%MIe@zq|I2jld9!=V##E`zJ?Bg$;lx7cX|fj(TAByH%k z*K=wwk#aV&C2R}GwU+w&QH>;GM&Yfy z3NVF4QKT2?8S6amf7F<4_rS)J;wlG15*-{s6#{jk^n)NHR_P3Ewvj1u^np}#6)P)b zWpyuK27b8r*aeIX?h%Amz|=5y*x%;n;%d5ZD>ao!%`0Dcrk!rhBY};J`}jv+ObRAu z1feR5vXI%oZy#<`%n4FCWruJL^cd?dk>I2@Z?@%ez*aa<|CwKb$#cDuF044c4qM2* zPZHNb{fbFQ)Ib<3Dry5hZupT7#*fFvp*34CBt)Rj(wraMj}NErIY2}oNj_p?(u_Ly z!>w4uxLeWDR;inO1kNi?q-d)fzw0L@xG1e26v3x^$=YE5#S>x(n2w>b>{OC(ZJP@L z;)1$?%zPz=(hn|8Xpiy4civ*5P$ogZenIF%Q(2*kQ-z6C_zL7RJ{YUz&4%SgQfrAT zpyZpLIu|{IqW5$80^pFMn>|#Qbi`G7xzLSc5COWj$B(iMB;cF55*GI8$f=%BDl_wR zl$WtE8|U4W(0@?;RJ3ZRGnQk^qo^蚄0gO!zIwc|zeae2fp^7Gj{G zq2VXzB4j`;ZR`Btu;-F*g+E#D+kEhMb-?aFI7OeHYRjSx9>&;a)QxxV-VHSsdFg}} z9P8xbk(BuOW?;Q1lZ~d6NBd+AmCOl*SY2bIJDl!74N>G{oOF0(uf1T*|@d42UwNrEk2kJD6?w;q6-=jF~|g6nbQDvE80L?{06*GL@HF z!A&!JHpl3~{YLJ7vwC$AaAG&-3wpB#8tovO&n1{ll(?za^X(gw06hDUg(x3>zSGC{ z-*H;9ly4!I;w?cfZrqr2|9;Q!Y27^73bZA3y>Mik_51RJLtv;%7y1AE@sV z-C@R7si>rM>-KGo6a^iFZfCoS3W~@~93msbAYN&Uj7(&Ge_IL%@kp%b=+}mZ0|yW8 zUSxyx$9l#w`qnLn0+o=7nt!F9d6u#QvQk!OCme|na&maruTM~Mw*k}$Td|Fe==@+` zpZ#owl?!YO_>_2&bpf8dqN0yhvMeDLh~{`#yxu2lr-4eA2@!c=v~&t=YKaL4y}Zws zQ1%8qrzL|V3XVn{Ko&Bwu`Mkv@OI)Cn3iX6t>EH`sKHZ$XA*r0IPM~Ji;9Y#US6D3 z2;(?L^p>0)y<^^gA94*wgiJ6|UT)|Sf3fm&W8<#t79Ji75%j2Hg4r+Z+-Yk8WCxQA zyuAr;{Ljvv6m4ottYP+bU2#BEHu=SX#4|HPYKl@*MW|uA;#Yu)U%s50n1f5x^N4@l z|317D4*&6E_1}R8Zkv3w`yV-|5{3yppi;qf!>WoGe!+{Koqg?E)Pv5W6(tYL%Pl3Y z@69yE0!l98YTGDeAxuFR*bxLc@O*o4|J>kzH+^NzivPKwDmOYVVScfET==Ese9{B; z7>3a#C`=98Da^8J6$TBWOhQ^KBQfKWA96yHsf|v(XQm-;hK(8kI9~A3%%7kAdn(67 zRYgVjGcf?yqA!tZV*E84plaC z{#c`>$oJvb2h>bX*8rc!tGK5DA4ZvgX?tVd6K7Jhvn8WyIah>0v5^wrupuh~H;;;% zTD%M|4^N8eg(Fy*kYm#H`RK!Q_$V^gJeNA?Tac4v!?8b{hmC-*G1IuQL8;UMR!=@- zm_8t3udJv*xEnaB^iab`MDRf1NhE5zH!+Dd;^MPR~bGE%9~vX`+#^j$WQkF{>M%Kmt$R9T%D J}2nZjD#X9dzS3I_wVxg z9^dajzvt-ac#i|``@Hvk-Pd)V=XG9BR0K{%3QozAEHY*C*ud_l*f>Fbb3f))HHB+e zIj^G8urdVEW0Mn6lmDXB_kMC;;r_jN9nz~mZ6ZsJIYa3mt35^|ppYhEq-SkN$mV=5 zTwgwY^LC=Ucvo0iN%}%#O^tbO%Fd6(MBb*CDlb=8W%l=_yu6~%aMY=B+q^=RSQJ%N zk<-JinA@tz(?)N$n9kk3J=*iR*qne`GKG`{m3-l}Hs4VX9Y(&U#$ETYY`(%5`FQ3PccobxV>Q7c+CXW!{O5l$1`) z`+ZUp`!#%JWo1JHgB!H8!B=kDTU!@ctqc+dnP6dIaXMMZ_| zwu+0h^Rx2NBz}?%mA9T=Y5BK?MV^cLWbXZCTd~5*_V>FNL=Y?-aw7;Ycr<+V@xqx( z=4yd=k2f;)$a*klDe`KBBdK8IY}CDk#`c{633|_01qB5)H8qEahfNpf$zP4(HC%$i z!c9ZtpH;KeSf4ieeu(|=N=m{r{3b`(Sl#e;iOSsyH|8Z*SVcF8Cd*#N|P~%D1 zd01xp2*KJ1tR5Bb+c?n}x_+`m?s$a8#Rye#2EFe$Zx)6y>_<3C_DQQ#t71}u=#gF$ z^!G!g8A8Lu>%EV*%Po2%>CUR6h2)V85P)F|n3z#-aN=jOvtiOK!dO~b0Q$n|) zp`k@@swn(?FmEhZa(PWu678Cmp4Haamivf1dmj0j-m$WMsz|BTTt`b39}oKOLqtOM zZ8q-!nQo|i#m2RM{0BTeAOFe6B(p^QiF(LR<+mumy|cr}z+h@>`dVtXEtJg1$EV{B ztzx!1pPlxGfA7L{gO>n1`|@bH#kX(Y8XFtS&3{VmMD0F)>$$Syz&2r1H_Y%LI(x7m ztsm|4&(9h(xo@t$k)|#8e8*!wD!Si*j0;s8-8Cxt`zb$CW8rr%EdyPqRXs__LCSEk zQE+fDMB?Q4@87$-yQin8M@Do8WcS>{!o#yNGe19V7#keCZ#Py^+<4s8(jqk=duxjL zvGFw4@DSl<#AJ^LQb7)2*qR$i`O=duLbiHo`0c}xL|Z_wbm_qQ`9E-vor)2H1D z+ycD30~L=$di)3VrynZ3m37WkrAgt+idVKwo$20jwR4kEc5T4NQWF{S^bB-nR=NF# z+2T%~{@rR{F2$by3Paqs6A42@#85{^$HiqAJ~n~NP{@8lP+5O4IJ$dY0A4!INxyvg zQbMBn9kb%*AxX#-q52CSJUmIK#}{+D0p*b&Wz(?JUL~Oj*qq!Cte3yLHmzKo=T4=) zLjC*wlc<2GKij1@DpI8%+aCrunV+%(}&Zty)j&ezDb9?LwytTjM7Y+IOR+g5F!MqQjrv0%SqH&_!y?-!raQf*KB0U#h z^-S}a?47;O_4t&^Qm1ETzDyX;I7Td}o*xBB`Z$~U8O27Bp*%)&*q%ZnH=d`0}bj|c!M$Rt_-XagprgX2fC8n^Z zaR2@4sGxCH2y1_Z46bxB%e{M!Yh#rVCpg46e*Ko6l=VW_&=}yg=+V*F=hmxOobOIZ z;>XFrR_jKruJ~OoAsKj!`Qf!Pd9wMAxy{&(*T?dG3ZuqVCa+wYOEaHw5)#SD(2v~` z7WO(nJxcT0AC14GuV#ya^ib=%@*CcamX=n#NN>o8Dzts>Nuk#K{JdeKx2};<(+Q8% z7mU35iiEPti{JHQ?os5l1Frd<5)*#y@^V@mC zGJMi4UtU=mO0IvzB;>Vk%PsE=na{+;1U_%MGM5os0h^9PX#d?7&nF|D-oGIdmht2i z96ifv^Y3auj6UGxjOh5YHd%jwh`zP&it9f=JNYv=r^6ZF*yt-O`)wds6*-!}E|%8x zygS|qIoperhl401E9+L?aM5hWI%g)YD4EMc0@!;mon}YSKmX?+fXb#-lG& zhKq{}_EP(^L4HxuWR;!1i_3kK@#;wF{{H^x=qUWi-rgQEF^Bf&8{)nqQ#B}!_hLRm zr)<~oXOjRf^dt+s*j?;PO-)_sO~XM|D3Gm>ey(h9Z!an<{QMap&(Y6hfu_T`@Ipkp z*wfQ<{}o41@B*)r$f|a$0OHI@;zNt2)_lSJp4)AA`rcQ!mO07rEZ3|+X_G?HtgNic z%1VnL@B5xrF*7m!NaXG4>|EOE6<1YN4dxjCO-e;Y1-Awve2Ge2TntK#CEeWk)pSoP z_1P+0zI_XN#H7GPI^Q$5re&~(IIa9H(k(ZSq~_}y9~b80QfXi&BOy`K*FV{ueYnRG zz9BsXaww2#F29d6BecSB-GUSs1exPBt9DyD(a?q&-d>m@S{Xt zi{<6zxw*M`ZlfOKyHXDy-nnzf5u@jW@WRZ@tBZ)p$g}g44JhRoi+zmR+QXt=`)%L8 zRVl=Dnz@;+xlj8b!|cL-5-1csX(((dY@HZ#E)u6eL`ZnL-OXL!OD>1Pyb!wZTJ=KD zOc(B#qxZZ;+58vt0d|R|`Eop({l3wPsPxQz8`vX0`UelF zTJQ4m@_Ktq$dPN0KPxtS+1+w{i}4_IKU$va+(8?3G#4*YJ_{ zDDm^fKY#u#FL#;oh>D8BVS)IB7^0%1Yc&3fj*>7oPA@9D6W;FR?*78w{&Qhr%p)dl zqsIGueA#`LS#nYE>o3P+;rr2$kf2Z{)zvroZFLL`CI<60K76>)rWO(sI#kWqtn)fp zC+EoH6n@@jBhgzeBnB(d1S<6zAFxPYj|LQfS9<^c|pa3;P{T? zoNy;@SpwmGo|{`={jT1{QmY`*d#5A+)%@!pyN+N&+}e)DgqXBv7Q@cGdho6*OG`(` z$2^n~01C%PN0n0qw;+Q-oLcKE8dLsTU$-|lHa0PtU0o%dM0VJ%#mSFMDZkA-L9*hv zpUBI~!U&{S({-#=kmO1T4h%foY`p^2=mzm^CML|*yOmeC%gf8VJ3Al0ND4{C43ye< zy4<;kdbJ+zPfki|YGZRU{-Q}nM&@U#sMlD9Rgr1N=8-q*)iR1rS63HOfS;e=VG`UK zq`CX|?jeq_^N;~hD1+0lmi#=45wclS)YOo7k4{c{A&pFZdAY^{i%x)|6m+0(43f%V z-FTIol9<@}XDf)979Y{m*RNg~(YCW2UyWts=8lSp_~JAl78myf^5H@IIqzF467sh8 z019`FV_a!&zfX&s%;<7Y2|`-{8h>6wo?__I^NKsOyq8D4rn?)VlDl(s8Xr%gGfPQH zIkS^eS;Wj`Sci#vq^jEFFe~NlE%KDjRrAC(*2>UuGUO`bj6+X)dOB3cxin8XAttI{OfL@=65!**7DG4@3&1MX7_9#;(zmg;1}xKQ<|HMBZg5fc>=~@Z z#zd|2pKp->Z)0Px%~RmPGVzoUD?NI&_*Y9*RHV^ zGsSdbU=yl+*vNaF^%=+uWE+iKl@EfBQrg~tHh`P=3U9~%v;_vl}$=YPNFEt4X2~*R|sZ690xxFkMVj5a(dy|5z%}zzH9pI8N`Btw)SljTv9T!C|cp) z?8-yGtGdt=N@}PcU-3#pb$va;tn&-6;=azn#)j)l9Q7K?8e&Wo!}e**t!QG z{mPXq`I^V4N6x5bK(*xffuR1#%lnC=pW8jB)U zc<6n+B;?ZQ^n%;8)!Mem=JMCRxhydvF~ZdgqRj36eZV+Dy`&Oen3l$>(H$XQ5Yy;jyppIn+g=g0pM*flk@59FWvriqNI? za&vPBcdkRU|NR~f7w}X>US1y9HtiFGnQ3aiJ9m_sNc$`+piVx@?k_31>+bFj;tEu+ z)-D4>fKHFH)dA-5<%BMpsHF^~&Ik`|kdB5T|1PX1Oh2*FY;SM(q>3h|q!{SycN^b@ z7zUh}aav1t{kl8oZ6H#BeDdPO2C#~+oqXyV8nXy707XYf$HNMFcyXV)C_jG**ryrK zOCZjWHJM3pBO@aVwTjrexTavAk9HPj&u|-X|tr67b$M z@h0R#?1s}<XvDj`8r_VeTI+l`uUK5HXuwQe=qu)vXMe%=D~NIvRYzy z#M4sKRJ)7QwTw~Ex<}m@>Z4ntAIdyjli7daeQ}GV0iP?;hH1ri;n?5bzt(wyORpkN z|5@on`^mcYg&m8ZNmxi&`&?OB8PJT&3@I8iFhmY{5nekUZ9g9wqU=+IBcGryiR9zn zxc0q8rz!T-l$~#=;sNg7z$5F^Z>z~rhl*vI+|1wNr?vIwSXl7z81Xzc=i%wjpGUnq zzjGEG8Z?TxwkqxBW-1*2#npolGgFo9>eW)`g`NPMD|NNCu0~t{gN(elWS}0DsllPm zRZSzt$6s#r_7aDhB;>I~b&|-ap<1y-?WD}Y zxWl~joSm01An_LSBjxOzEUc^!+;0&S5%xoz;S#&IuHm>33QT9HP#Yv?P%-J~=zwqsJtPv7xJxMP^!Iyl(>XOM>DAaQfoJ!_ok*5oM3U6wFQ z89Ab;Z_D4_{D~UZsdY>@Gw%J%aqCfk;u4!4TfScY(hnE7g?>|>cyTv_9l`5}AHnkN z;Khd%z>(@V8hP*EpQyCCe#7vMnlSLEi`n3t@0I@bieE%@+>+jv0xGKb#kldX^Dx)N z{Fw6V`*yzYj_nR>&@|C9(6(5PHJRaNR#?;oDF9AzJj1Q6?iL9KEF22Ttg|5db;uUHKPsyeD- zq==?`zAXzM21?MWM{X|aQ~PgzQyQAm$jY{)(0g7}Uu4yv@gN)zfk1bdwQ7G^c*mkKW!Zd>`Ge(nY!d z`;iE-oS2*p>^g#qS9XBn)_+26oUQCWTYv^Eh$hxhdEY?7g0Pg}rAiI&7!~gx=scga z&WF$?F0{DgRvV&Or|@t7M8kP4U-LT3n~qmrFgYZ@hUDR)c7WkErys* zNYQ}yAhx@Yx^I+k4b|ml--ds^87cjD{ML z(m$xlPCDL9&;xh!TaSn|NAe0gGpUh^A_!Hd;~p5 zCI#C{t&?>vI;)No5??!bg$EARqPUgpV|z6hJy6(@86M;ksrwdy&WCF3NmRH zK9E+Y1hy?-R=e6yTTJ(wn%%pX6Y%S+%*@;m_bPH^bU)(a-@{jm|IUl{+}~;SKiqF+ zS9o&ZO8qD1ka>{Q;73DF)4%AK68WX%-^44)#$7cyEcmj8RD{<-^Jq#$XucEX@T;6f z98giuy;=-3rrd`d5)u*u65`uVv)i|GL4@@5@Ssm1MWxr)K44>8nriR@0i7}kG|A&5 zI+%EPNCkIS^2!n724^L34KHkcfabv8>Iew{OR$}wRrPi#^c zqc}3jpWo%_ujqeQ`3}mFON5xZV88s#{dn>HNq^=qG)2_3IO) zA-nAz?%>jGj*NKe71<64Qz$X5(yivmrnz_jVd$G1?V{4VLe|#S0P8^wfkG*nJ8)Yi zMbN6B$-=?{gc0TB-vhC6agY`!Yn^X{LW7!thUDz!<@YV75r=+n|2*JoiLIwwnbx+( z?*UjFf)I2e*_-TiLP~-^o|?VjE|YWA9LJL7{%&TT!`shGmFP{1q~)ZKhOU0w>=49D zx$&B~Aw%GO3VrcM&2!ZseLDnJft1Y^ckd8{Vy&lWXp49e8bcb@exUuQ^3ObGtz1?I z5H!eW^{G7Q8E!QO_EJ(n(Y^Kbk_@;m7PF#2T8B$El3FIWsoK$onuaC~$`3G;j~_qw z_p4(E%p#zj!8ZW)$Gn`+j#kPJzQ)(bvUe1pRTa3@}iy9^*q1CMGAUBpdC!*3iJKTu9bwX7)RJ1%(=O zvU|QTp|u8t!=^3(oI&iriog(I3Nz(5gx0eT(W9Y}&^ z78Yc>%0`q_RA>^bOfNLw{gj?g4+=HpY(SjcoSc}bOOiJ_N~Q2* zuxPCT6S~9H_a3Jx_x3>Zx8Hfk@@4JrgseIvN=+d~ zr--7PyEJP&xfbtHD>l=qT*5U+6o+jq`4{TT#VmQDBB0) zHSHttPtDwN(A12Hm&f|v<-b@er@tvrk)dc6v};6vgM^$6$+HXM4fJL53vLZNuo-YA zUcS6NjDZTl&n*2xiLU|cl-<@ZYdT;wcF}Ni9QsE13W3`{oJ&gMGSlBH{&ZYOsciFn zslQpT^ej3%OSm=GOdU|@$-UKLiN#H>L0fLW(j&`a3 zjQ}7Y9CT@Zg$5eNU66VB_=bjuH%zX7P+}00Pj_MCh)dnr9uvju#m@ zI10Z8;0Ox}?z&!Kn6r~yCfdUOi}{i~8~-@3<{H=M*9DzJcRf(AnJu77J#c#S#z0Ok zCyQfvD0&6Cdl#GFS>HV&@+|9TWA`IuHGYja@uQJ^$1ih=Kx6sVCTC-cf>e0Oa!-1FCZ@dG47&sI30HHo1jh5%i+uEZK_2dH z(vE!Db@2w>j9b+5ks>@-3Rqb);1z>^i0WK;s41Id!PF;t{C5;N?QQQ}B0e(?_r{Pf4HltEAcpMYwvt<9lW2sf0Ilk@MNxma41iH>#|XgQn|;q9hN|6P(+xEHV_ zgmC}AHUm~dB0Lx$HQe$%h`BVbU8DT| zgkT`k|OArrw zlMJMx4%d^ESjg}L9U7par*LuPkUg+;xQND|Ol0Wt>qRgNh3pp$xCiK3%gP`+xHO?e z>nSvx)SQgpRcKP)42GK-N61u$pRSx`P{`4f;I;_|4AP+csbT#5_|HI(Ydyv5r^ipm zGj7hdSMg{DJOVNtAgk(qGvp5!P;8K7Bf-U-3qa2oz(nu6Q$b81*_%MZBrTzd!cvxW zo)dm4-~oo0Avy|lKMDFNueXTjLwXWXzZ81zj}#QY2vT{|ru!N-B=cXbY;|Uu^ge*3 zr`IBNc^6*L{Kv(V!ub2kSt|13&2qXcWJKlhI@-~NPKys5*v0mOK(Pel1mF!4+}Gb8 znKN(P8`V%?1Au%{m{e?rsXJam!i3fNv0YlXH{+WYCi<3Nz#szO>=zT@<}c?*&tyAB zoc52q_B4`n=91w6GiR&o_EyqR%(X`R2$!H2GlMKgPm+`yZ{U?a?Psa7+x9PX`ib{& zakf!?eI-7@*1N5;cmsm5*MTMER%J6Djt=67-PGVb!S2jAsML>9pkilY6ugyC;Vm~IoPo|_*gfB0^%f5fjCIzOJ1 zeuIJ`Jo?F_J8ih{mrG#9(fg_;mWli%c7>ky8QJkoCaLxs8CH5qPWpfT&N(ttAhKgX zRY{0yjxR4_{_UX?ax3#zos;3-2Q&os!R0a{!CeuOLN9LRu$|Y}chCQxaJO55qk+GZ zP6!AB#G-N%*Ips5Qh;SRO39V|(A5~=4{$GcB=g|i*Y6`{Rr!e{Uy5C(ffU(yWlxrn zbVU|9I26XU;qz5p{x0gj-_>Sh!f)a|OB2xVeh|38nGJsCN0*n2WUoBcs#}ck=1zYai+OU#ZR|s3|(!J4iZD zlKY32{p~kmo=P^}cXx0kOoG0S_A(Ttr)TI+rm)M2de9eqvQO@Fz1#ezHPW9X)BBEu znHAxLY_v%A-^Tg`UH@^tkW))nfivnFZC?veg`SH=Go28VOcQJ#3}qUKfn$;X)~Zn& zt;l#Qz5Sp1PL_;ZBBjQ_KbMeKtgJcYe|l=QE;ly#GHOHIQUC2?$M@WM93uR(cRI9p zgvNqd_9RShL6Fu-EF+hjCc3@&G93Gk(?8)0gJb9EHJ#t~Iw4w-MZZZ`z&2#v$tA-P zn3Mhhmv-LHZZ*%J?e&VxJR85~4NmA|Z&03JhQ=cy(QR_`qP#zBo zq_hzJ9J;F-sHF9FiSn#_jQjc^S2`=Js4v~JH0w3!CPqIR?C%Fim#xlr${qcPi4^a2 zw7(zSodiD+g-TSyzK%knBYr5cKMpr*%4%z=v}iXSM7RlC54{`lpFyc}b}kR2g|n6X z)3=b8k`Oyk3JnFEf@SAsbs@B`Qv$J*X1IDG^RjlRS1Vc5k|o4|OFNz~9r}-$V`NHd%h=fwJA>*$Bm*kpA6<75ZJO5BG7%wDw>6Sxf_a-ZaQq)t-V%ltWSf6u+I ztZP);h$x<5P`{Qk0cQj#@2wZX9e=hwL{2R@_+)bj;*4e;5VXdzIHP^FnAu~cv+P%D zH=!2-y&0x=IXNt>dxzd)TphFM*H#X@w5Ft~BSDAgo^^0>b=}+D1#8EQDj2VbgE5A| zRx|jq5dGuSARd(Un9ghX*^?JtU4&g_Z>D~Abtwhp1}vgED4?ObdJ~s46w|$iT@g9F*ZgM-Naw8o zwtkdKF5D#}BO_30Sd9RrZ+JMELmc@_p$`HDq*aLwaQEQHDFhPKhy{GOO|KTl)}*U{ zqkVfNxce`Nlb^FO_xXF1z67oLdnct6cn`M#G7 zGBo>-0)>leKFOm~+fY+JNTSw=*g9Z#36@X|{rs70RWwKbQ46*%@=s{ zlDD8Ss`ld&L@zBRzJE`75$@;rm8RbPzzSr?w_5B(7pg2Vv!ZaDGhU^J+}}^ma-?_J z(+8@HJ_fjRPKm3-PZic1G+j?34?r2BP9_^qX0+TxK~vxou0DAJuAB=_P3`lu#TQs> z(*$=gUZGG39txF*ULYCgr`s?}Kbsy^5+(aqj?naq`Z*nx33AfVkDot(27KGtnA$zx zGGA9$2Y45}!DC~3IvJ1i2Yt_u!B>_1{yl4<5E)s_!Jl!A{Awg!jkTnMV`?u>DnlX; zo2Fo_asTZFVc%`~=tnha?|}t$duWJ#$=+H*$RXNp+WFbh^Lg_e#xV0KQVetcuDBzL z7GwgqoPD9Lu8w|v(|7&`F-~LY2;*p9Mj!NLO5bFE?^m(6U@rAXfZa!?VxniL0I@45 zyu83l|5k>$&Hqx6n)#;0=CLy2JizwFH$Y@>jgLs zOSq}S+gpsJKmr4;HdqY%%YeIRZDWMGIa{fjiKXY$HXQu4-eBUTurU1jV%kt?bQEN~ zF3%otGmniSdcbi8wA{brWK4ZGn~;zIdma#6)n4faoy@ ze*zbNEo?$yk0 zIRtDT4sZG79VBbA`+yk|e1br=FB>4++r~fhp(Xw7*|X=*u`cv=5nU^rtRNN4%yDqK z1*$R-4>|`01#P~pC@n1on@d%dz5Cn{-Hhnt7f?QX(1>nDKf(``I=xsTTNA^T-oV)x zeKnBX!p+PO9b!V6f^P&zI8cg6%pSN(XTa?jd1H5Tec$BRdN5K3kzc!J2G$F>%*_7& z{R^wjpKOObdxjytHA~~)I%_$mjtM#d3c~K_=;-j^$vE(t18`cfd9lREXY~O*h%mvq8s6T}M)peL%QRqd>PT$yl=M^#RP%$;S*29bY3E&N zlky}cKCXRi_sqxoEkp|V=Ap7&_WxE_Z8Ov!W%b1t`obCwqQWG0@%}o!1n=y>roaFG zb0RByz@d&UB-qCIJ+C z-h$1M{bF7|s>x1{{Ud0n$i4bu&ve(cfpufG%ZuuN{a0&iYcEZn+kfRj9E24tDY<-K zH#dZi*F56Qn>Vsb0GCB&p|XGz5?lxGpogrX(P~}}73Kptlno6}4qk|pPLB5%4$=5Y z7?+PiCIWCGL5~|KrLz~_KIg7N<|ko^$sUI<-VAFC&I<{8OZaxSwyQY~4v6FV*eJ3v zsqOCNBz7r%9sYN{L*9{MUOV%I_zFvUW1{)17^OPl|f*>e*GH6>%pNR zqMKsg-y*2eytnbf_qfXMHWqoV>_?Zn2u=R@v6>Yn*5B8cUtHYV*~!Mq`FG1>Bhdvm zjhKMI+10hEsA$bp9XjXh>+6>;5Iw?&^mR0Ztxyc%m_Kds1RF|GcJ}k<$a6l+U!bED zo0xEFH`;u%|3p1t+kI*8x1U&CT6(!W$Yx=&AP#<6=tzUa@9N;NsNY}o>C(gFqKN%O%?3=9 zKwrM;{CEXaw*7Tva%FwJ5n3M5EVl1$IC+<&#tQNyltR$TU?B8v4vtP5G1yX6A-zt3+&MAg*^!O3Q1%mUdL57Qq~ zw(C!f)uEZn9&bZXnLs3}WXe;5!wJUw*5E64c6QKFU0+&~&sHBw4c8lK^r=rvOM}%9 z^KtthkMw84Q_pAQ*C;6=>*6O|+Oa0<1(5So08rk+`qha2S1!q$$mzY5C^G&#l?q3b z)_q(QQp4Q~kp|E#AO38(W{;1Hi%U$rZh8#S2-5`)FTs;{>(;F<{o6|KfBpJ}N(cxD z0NbQy^4R0GKKln?6Fl7=P=(Eh9*zA;dB>t(_=tRnbY}SFMUd7T6&5B0cL1&qiyz4Q zQg9@|7hdnGNba9^0Fx3wA$Hcsk4)U$#igZNXEFBRB?7hB;Rswj1QeWVie<1lczAf> z*P9fLJD}usw6{Y-qzHF~xP}hC?F>r{brKRE2 z)EfBCwS~GJT3T;7Xf%B|*>1$;rve3LNA2@894QwRSXsnUZwo z;ks<+!WDTo=%DYfPYT=o{&akNd}&Va&sGF$&qif`5nwAJp<}Ql7r0;ctzm3r>Q1mHA_@JU9J6l^$6upSZ>9353gxE-Hb2B|3ANj`5v%{@t&@So~J6Qz_lx5s@ z{ZOVM_v_EGs<&ax0+*1`dw22iw>2pd z#+B!C^#_ylFE8*o8iLsRBIMJM^6Esb#;EX^;=kGZ{QSTm%y>q-y24a)%XK*A>yHBzf-D(iKWv8idlA{V0I zKCrB{%8VP>a(a4NP{0n@n1CSs^OcfZ_PllF_3DbD{Hd<)Zt;uLokHzm`{~BZ!8kZ} z)=FQ5Yh0H4zr5T5*T>$s-wdLn4fd6gh^Ao|)X)$hoC4jx7_Sh?rbwNv8L4(iE-KfnyK$_vDmlrs2VOoHRlXJDO zxY6H{b};;SNFO|yuCA^S{{Rn9-Zm9M%Uw{=W3CMeh2jHY{k^9L8$}4-O86)-8q}+$ zf!x+Ka1D-E+4c4HpuBNIoQEMZ7>r1OJX2p^4+m{#DK{qv8xvDdNC+G!Ya^u? zT5ct2!O6+`&!59ia6tb#Eh&kbj0`;Oke>iu1exsHQG)f>C3L#Z)#f+yNjFP25?o$d z_IK?Pum~t>>gu#KHQ}7(-NoD9oNWc;1JwPofq@-x0u_X3R#fcHd<}pi$;HhLe!5S= z!5y#h$he+hhM+<2j0_2rm)=BJX zPo*FpLHP&b+dWtb!9n#Sf?Xj_&$jT>CqjIDAjM$gOuhFkD2L&%3!Pw*x|$kEqSE=< z!Swk0=7H9dF8lv_38oQlgBk~zQ1C^3eGfT~iq<7Ur85n%@_l7dC^*Xroo0~vT8Eq2 zG{FkF$p`j_OP6iHS2Y!?3ip+Q&ocGuxa|CuMZvs0JQ6tMwvLWlnKI$fYXqa!XM(jq=(|~yT zQkwE)z}^jN{bqZq=ic(WcciD;0NAim&BLqX)x}xc8;cD4QZQnmZRFz$-f1{BFn_18 z##f$X!vR`TZmzbT-q^wo5eQd1Yik(x2#bjL^yw3r4{oDoAa6htIuu*z`QYr}@TyxM zskRTC+JGON zTY_OsxwKBa-{Ke;_%8C5rF%{Z>IiaOx4*b?NRDfW?1U~~KCY$-`(5Q#+Im-TgGrrRD zI)P++PSswC1pQ*3|0LveIINe2-lPT7V7sbb{pEQGke1i;PM;V|PlbN?FbQ@C1x3ZE8xJz4VuwCW!C*5m-xE_CwjdM0 zg4o#DfMLN=3S(j@6o2v>@Q;WM80pD6EjOg#)VT}gf;gD><+d4J6g1+B)mVK`x8z{q zpfAS~0}U-I5|YjB`EHm<1D{F@9UfS!TZ4!oubx0?z?g`Cu54CcmF*~cmNWRlFWtTX z0bnfFsa6CMa_~*xy8;3W4uSw*D1$zT`RE~)c51$H@$m(B<=NxFKw=*lo1M8c=Web1!V#Nc4*1!lEO04q2MbQfhfv2i>csSv! z@ee4%Jw5h-P+-vd*YNO*{|PhvQXKcX%c9%T-Wf*dUv0_63w%fNC`m*GveGUwb zLdm$}6o9pY9TntPffuQ2thPf#Rxr5s2i4uAxSNISzlXgNd zLFW|w;){J*kavkumhRa+0JenPRw?0VBIi3dy`Rgedg?!e&b?5<=Q!ufF{|oocQ7+U z`7L~K{fXuwEn?^|5MPe3X_gBXBbdyqH8=;%f65r6XnA)tL7Jhns|$?0K~ZRCaF$W8+S(9l6lyht&tXQw+oqltx6Rbi!Jz~|KloQs zsQ;Vg_|+@ox~z$6rjcW6R@H$P!u#0&8$cO`od!(f0Lic*9Q{~xlXeE@#*{T`xRv3F z$pa-!RSM#$C!zsOAciC+jzaze@d|~4&h$0>=OZPEiQU2K;&H+EimZo=F5sf>z!9RN zq%?c}-1Xm}XQQ_#IrOF#vfUb7`x`n%;}W|Vck15vzyu&HJVapqdzC21`7Uw~aWFdA z*}-&y@A>IV!FV#%CfL!zp3ZSFPQzn{+cO;*R-|DDL86@-d053_x}&CThPaUNah ze#YSa1uglBy8yYWC&x3*CP@ce%oZ56gi`qK-8)$1tS|X7Y^+&zDNnM5gu$`_;0_$d z-WEWs^{egC5EOJfQd8;T6xQ+_G>zJ(BErKJ6%`4%)Q9|&EC{1J&5EYBe84eM50kbq zZVxAmCvl>xyj>V$d@uHoJS7zs3}^zx`88E?>Spu;m@5>!(?3z-#zT1GvhX~EDeL=A zCMLH~#^7;fSMpCA?7CV1A!Dc5j6A-n)eHIXhzW*`AXl$}EePg~#C%T=0Yr~amS?RL zG7P*S)-5yZ1gdqgHeL<&82rXS7HjM43s(jQ^~D-sloX}`>ghsFq;@dF{GEb*a>EDq z_g98h8KS{0?=tzqp!BJR1HmyvNxOza$R*oLvJew{jQL3i#G(NA;tE@yBAroElF|8{O} zZZFUn1=kBmc zOd&cw1uvhuK~4aULK5DN?$->IuDctC6ZHW>C@U#}T^*hFI`NIM31K<*JZuW{La+4j zyd$4qBO`f*h2smDDQc_$rf!9|!@z!jZklhwR44ijmJM@Vr`a}eY@x&!AW5zu!$H+W zyPysr+i7k@|A51Xa4$E&0t|4MpC9IM!lI(ChqnVObsYDZnmsNM zG=pg$G&D3gk3dJjrw<<8*Tj!2QGB-UBl%i>3SCiS^;;EKtKM z1OzK}@}kW}`qdyGBxFd#SW=)A-jyrCVy-$XV4$!@+C*;NOasObp6xd`9*~Azfnt-W z0b77+5m@F>8BxRr$OJ6o?x_F5f#l5ZT``N$b7eY>7@@pDtpBK!%g}jcydjgrr`S@|7(Z>r)`LA0AbA$JF#2TO> zW^8Y-!bA#;Y3SWdYz~|Jr;jY^2^I9Knx3ErL6|<(cOd~T|L+Htx3yLX_(G$iRDmDt z;)4~6B(x3u!WG;0QUbCI8_iY1g*96aBBG*@H(|RreIEfDfz?Bh)2)tAx);vTRSHxt zbrfr!xAM5swDuh2qB5iz9{8RLM#}2L1hJA*M0WN(9Gx*kyvt$0D?~)J51g39oCSLT zs@hvv4EM!Mbx{tzcw4o&vT{@07bZ|>(l~Tn@r$@5ExM?hSc}%F4lg3Hp;G}SWfm^3 zH@ypRK9OD!_h7jJYIuY@%gq-bGwj26mWK)n6Pnf2zJ>q6D3Ia-VE9Rtl$9+9KARRo z_ocpk{s}z(qtWLCOn?m0tYFS%X2w#Pze!HcOd6V*nK|?9f&2OdAmowJ2VfNh)CASp z!8Z#u5>ra>M9uUL4r(I!fn@-+fnk9E-?4TAQY@{oTLg_jPiyN#CQ?zK<7=q&)vrM} zl}gm1Zw+aoytH(*!~|mkdg*{#6|?%ZKIzbWJ*9l00h5C8UZlP zqEJHYVZi*!Qn5tb0U3Zo;<4^jYmlKP@^Tv!_4eI6=z|HD8L-E})bb4$c*+L&!ET`8 zAne!q61wkzg7oQATX0fO5}zW>i+%fMK2hU{LIp}~Ee~;^B*01b>=}|3wgM_i)*GI{ zy+`%cZScV`7PQ5tX6%zpFAiMio(4z zas>)9P++Ka&cMfjj1^SJ(Wt;EKBNbECiTMa&nSbw=9B_zhr0v_hrnbJ?8LP1iQSVY z$P-;%D(^ln!@AwWtv?@{Fuq>sPcZgO73tL`6hEH2wx7V;eas zJO%@udY2uF@IVIu;BWZBAeskdA=6*f_tXO(rjeoy^9-N~RMnS30ReI7r#a{+y3OUOD0pwU<^cRlY%mH49Kbj}0t*5< zMci#WHG)af>{Mxn#H1w9qy+BY*VNM54}F^hj}b^fxD0vt`JYx=$30^5pOu6e9eq7L zn7IIv0RxEI3lNVXykQjL(x47$>!bP-a8Y;y4b0hHyL#0RClXNQPpHi%s&1%eSaBHm zy88c5dp*wA6e%GLZiTzog=Z^WGAKuRNcnV#u$%38^hfCVz*#{RxWn8{@;X7jj=WfY}w;W+=`e77(<4e8lw5EgUE) zcG{XL4Do?ex}=&1WehR^czA_8cUey#z!POgFmj6KyB@p4SA|DjAe%1-8_)Lj!P@yH z4~CubI?B*czzBCi4nh4ty1qKD%595wBM3+dinN4ENC`@VgaS&6lF}tDT>|n+3rK@> zOG_#sT}r34lypglfW({o-1q)``*$uUuDZWiYt1>w9AiurNON8p7+}zyqv4p2cgQ^NNRyU}rgJ+g)YBAxTv$QXlj$7T}Pk|_64ft0Co)4!PV51lS*j8B0{gs0T4U^r}nQ;jwoKfK6;YkA0IN&Fb{Rzea1uzwot$Q7P zV*T6(O4UJ7fI`FTG2P)6!T|Bw2*_!7jy-B0K4m9^-skJ-TC`%}?OT1XYIql*8|?27 zm%KF$nc>Jtex3O)9^+4&D0Hm5BDF?{62BDz)i!*9{{+TdJFcDcO2j}Nc>a8V zT+bsSE-v>}oRu^P%D&3AySoOD(v{HhSAd2nP!b9);2KYnK2G?59i!E`cI8*VrtJd_ zBMAw^wLl1+Z#_J~MvQR*_YqcC(q*C^&P z{zWEXvra;j#qUM-pXZV!#KzBFdgBGLP)i)YZFo`I<4!)I*vjR-tmDQDE^NAeUpB_ z8IN0Cy=S#;%8is{N|6T~!aZ$jiJ2^dkTqrjKO!sZ208h^lM`tBltVQG4UGj`Y+@{4 zO>p#q3=^zes-Rp}D*(SPbF>KJU|l_`>9!6cR2QK{3ifXxy7LgFlX_k-oIkowi0w0A z_WAf}3NhC3T`Y-j+MJZX#;o`xh@~^(7pyr}yVX|?2WiysuZm{ae<%GBE%Z>y+4&HT ze+Xofy zx`gz7Rj=+-2LFJf;hDU=6=40)U=eI0!IB`9h7hl&HZld9cDgn`Cgw8gePEzD>~bJ= zKqVl!VE-Di%h^}EnXE{Ti{&Aj+BQG1>gY`m&7{>*q3Rt<@3iIg0kg?A*d#$2L;)?3 zUJzi{>FSB`i`1%6X0i(k9sir#a$j{D-b)9IG<0(n>Kq8vATB|PgTuogBS@B)_U1(&JW$RY zNMoS_{}O6yY9NZhKTuM>e7j_i>#dC;K@2z;WLWSK7XAmD5$(T%B>@niaNqZ7xAnpk zO3X5bPnBy*q|k~pjs!=)1s+r9nyD9r{sx?)1B6&rR9H~cFx61)ip93fawYaGbig*X zKQo}Zw{!0;&B+d36(adEBe!Ug?$xVTP|g6A1EM!Hi5X0zt_Mm&l1@TG0uT_0Zs6xO zBFly~`~^xr(0BkL1%3irbb9Im%m&mpq#8j%_Dp<$jGYu0re{?$3nbzs5o0mnUMgs{ zC^w|Qwn_A%XU39%_Fq&-^p5esS2G5=cEeI7Qbycni}FZQ3FC>130|vt6djGAz;`O3 zeo57YsH>?7HE^`s5Y_2kMNr#llf@4NME`y8@NUuRS-xYz^-g(GJPO8siibCeU|m6p z6HDHC{~0UhtiBpaCz;uG7Ng?{H3LdGeyBB z2JCH@u6bf2QQm&|cCwrT29`t-fSTbXAu52XXlu(?ycsm(0r@r7F0M?8UT6bSdMjLR68_s->e=X~_@7<5KId*7G!k#3=Y3*H&tjVZ_)S zsp0Sk)$^|YSG`9-O~uY>&xO0}O0>>#jb@ytl8ziwSURx*DgKWiKkTdEhQKEx>KO=2 z+O3apy}@k&swyBz0VV)H!Fd4EwB1s>PYMrEqib+bdH>iLxt4#XWC|MyvtW7b1O|pB}+&p0yQdnY%r5EVO4>_Tm zyE~jNF6KzoJ1T;xj!$X@F|x2>7q5rGt(2AheA5e>ng7N7BxYt~!@ZpyAQ=tS2Mb*q z)Z!2)_La_5y}$-xhjDE|%0}b7$<>;vO*@ygZfyREgQ=+d ziPdc2b^q#u!FN=>czxMHFN$}I{q-3zkpgVbv~%$l+nm-!8t2~M@0;+u$cTmrK2?*k z8c<}buy)|fn1C@0Z~5(b2bN!m04!7OoFTwtkZ5 zo2&T@2y;Q$b(!g_zZW((IDk<3h~XI0m1?KQzo_4>7WSsItbK4?sI)&|UNAa9Ha47v zL+3-u?XAU!6TNkB24#F*c6C!d$m{8lesly#MY@Kh=1Kdw`oXtwXir z_R#HH91$wy2l*fWaXh<`McXaNQr^-uoI=>bRhIWSbHJ<|Sc@?Ly)-fX8k%#_=^D6U^1Tt_p z_Dn-OEcQxRWTlD~p=+9x)RcenZ#U$mo(nMt=;wx7{RF>Ph?6wO@kvG@G`O1d#3dT+ zL;<%+@m{i%CH9cvVnKxJF#!w;+Xv&H!4RHL)4Ta^03QWN4zC|9go~q}2T!CjWyG^? zlP|i(%t!#?&-DYKvK5btaW{|%3Jb0Vmh>*~b;vKl%||?yy!HI-!|RFd#{DIel-RWt za`yJ_oo<~7%1{qS(KmAuR#Oa_x(D6MRtI*X43&uC7v_pLx`cgNawgg2r>ZI!r6P+OFr~n)1<;TEs#I3GA-4B1nmKL_`Fz@1V#(jZpWT8 zlzeETG&F) z82%%IBbQCP8exJIqS(IR!rsP{ZbwAxVHWRi8nwrN1^-scR;FA_*)9F|K`rE5d=CTP zWr})w^yKpt=T!Ye#QlN8gM3RbU0cOF|DJh>ki83v0dDK3tnzPF8eH{M~J9cjPZw2wX9 zj!#vYGQDJekI4I;b-ep6vAS>O#Q8{8_s^Rt!bI<{;@UO|EPiWBXtJ8T!HnHV;vw5k zN-0;t>=HIMz*UnYQ9Ku4sBL^yP!GvYYGqVTCGsm6$CSZG<$$r5Ov3)_n{B)eM9Bwd zkQz(E>FMLqQ>NqBIgZSjrU-%R)tiM>J#9MH(E@}D4|oi7W2Y<~W=+(snSAMSZ^6Y$ zC?zWo4o`fVIDC3&Yqx3qW1%U5|C9a6|05GLIX0KOg)e^DeG(96{o1Oo%#~60+js~D zB;R~obrQrX&v+briht_;b=}-4KlXgFBV+U0CYI_he{!toUBvN)&Fif!M!DxGOd1IE z>rR8zr;lNc5TOBS%B$ek-=Q z)-z%TTQw2prL{RksypLv=Cs~c-L$u;3-RZZp@Duoho&XVQ_K05 zR`r4WTuvNs`dqUAzOB&Q#tfnV+sJ$)^-tvH8r|?k^fu}64GfexzH)HkWvwpC#mtr` zyWEdjy zOTjdUX>{?vPF~Kc7PZ!(mt~0kB?+ZWkd%Nyn(kZoxM_Oad^S(M28jd-0q^3ymw%DU z-&k{(F!4p(0biGU?D(Q#D)0X+_CXYq1)fENinQ%`extzQ|l1_x6c`H?S)G=;PDDEm&@U( z5lUWhEbRM5pewEQSc|qdvxL^`aeo7nFGjv>z5mNq+ff8Bc(?LcS>q~}Ln#0kbb!) zwj3z)*sz;cveti~K0uAdN^v8ZAeklAmuHAdNc=%skZyUCdyB$(kI6@qYr-*gY)}MS z{f<$FlH%s2@UUpQ!jFZbOLkV$+XMtz=SksVd&}+1e|l$LONRK|!sZ~-`22}2j}ykcTS+VA;~M!k%OHRlu#lzEC;3PQLs?r&rAC{p(`mE$l5q zNAc88aXj)5KS=lI!dItZqjy%VJ)Q({OmpeB9=<1sF`44kcqEizNbeCNH*RZtXG9zA zO}Qf3^QON6S8s_4Fbm)a^N15K+(NJ0_l6`I-xU^@&4wV;I3eR zJ&~V*L8_kz-yQpQ^I7xQd}8mk)+>jCe)R9%uGOGTD>PHfnG*7n({Bzho(8uwtQBrd zDUW`6|MF|EJ2G{5;4WuxNOMMRa>WhrG=HpK2Rw;zExeM;u$R?OiU^t>Wt6bwcfE=H z&beB&J}oy|5cjfnz|tt6B}Spdh@Ct7stc@f%DIq4K{Tnen0ijF%12iXX z&m41G-pw(QR?_>%_)`-%dkO918c5j%@0H^=J`YGgL8ftiy)tQ*!vs2+h?zp|ulR-z zVquBX@Gykh+a@%6?b=ntkDDwr666!BhB!KgIl&lK%Vsp^>wjm&z9XL&vLp>7R7Q8T zFMsydtMwGzv=iwk6OiRytIPi@Ah5i6&qtfWA8iT@$t8TCAO#gqe!gtMu$roBJm7zs z11q)yDAWQ!eNYAf5GW|0pwa+cAL!L0+W%`p9E&ZjuY;Wzi>Fa|;=8ws>7(M6cjGHw zWS4(1;4l~?bxicOE;fe0yE8r-OG+eR>5{m9zK63o&k~SkXWQ6l?s2K#wmMvVMc|}( z+M6gL&29P-jS1<*LUw-$yay&E-GqOOTxR^Ga<*UeWF(Z?{(PH zGXDu{u>RY$V|}wlC9y=hVJV5?@3XcGA|HWGo|ssm#L}(`z1C4=R2`B!+IjGUMWZkF z9NzR$ZX4gnKtaJXZZ)!gpc-b*lK^-E^k#u&GSt_1uR7uPZ+&|);(e^R=`_>FcKG;R zrZ4SZeiY&R*!Pv}K_Meju@CwQ0we<} z6H~J_cH)-Y$NVF&*G#xIl|I2nsU|q6*<98c_tArl$>`-PwyupQw^^w8T9Sz|=lQ(6vq_wE;wAu=mHTqCOJr2k4@2(ZEQukgrUueyvFkUl&Di?EwSWEi z8@KCwFu&b=&!ol!{t?C47drGYN|WDtU)d{AYR}&c-QgqTA2T=1OiPyN?`^a(HA0S* zbm;fB!^XlyKOEUscpP#aaOGRRdCgkA=GztsC(J^b!|jsyfAgoQKhZACx`q(F>Q8Lq z`UB0f0HT|n?d?G421{&c4t)p+kogWu)cyAyG)9JszRPEMSGjpHA!>d*1e_tYtF+ud!R zzqgXUw-Q*|oXbh%<{wkfb~Tvj;&|oRXuQOFIq|!1KWd!BJ?ItU{867`i$=wJ8b7vv zv<;7_PE#v@7HN%XAFWSQKP+nCOje>HLT7`oOMno739d4ttPD?>2B-tT7Y7$uhbrsi z#H)1}ann=+RMx~6ith_TZ9^*UsGjw0rTtyWCwKJzyV^h&-T2Kv@zI8P`X>8SQR`TQ z>YOLlK+QIhVC)O20@+ZR^QXrHSc_QsmU$$Y76J@Hc9M>Fd=h70?``I-kIPLIWaTl^ zY*nh6@gNC$p2x4Ci>%G*(hfX<0HPS#siTC)(0m49n}|-d+prY6j%b1tU!sE3bERaT z)xxGZszJsWcfVf`a4$JFTdU5w5T4!{>sEV=+gR1=XrP*qyC$&4Md5psH&Q}K!qn9! zDOh9q3#U}1{qBH_S0vJ6^oqy^FHWt>hPCA>NR(x=B zfS=?BP}Y8ak^$zv6<3`=x#k9R_@GhO0S>0n8w1vsD@VBjTv58jyT=+%viZN7xL+ky z_3%bwXogJ_X+Az;a=43YaKoZ6h!^MBzd+3-O;#vh_Ea>qSy?xT(#(Gv8K`@r8k&$1 z_sEH(e3f>2I+Yel*%o--l>5F+@?hX_HG7N9+%Al$f)n`VZ^6S=Hp?EGNnkC0C!p`O zJ`Yt7fZWbo(>KY;DvUcKvlR^bZA$drEX8rPRh+tOJZ`qA6_Fn($vjE?#*tf;H)~{g zKhM7Ase^BtU5xHL>q8{qx*4WmqKh(h9iMfqwOI1oUkY2E?ie(`E8kRJ>>uq#Z>uoY z-EXHFk{Zn_sJCQOX8XHluGRNJdLA13Xv-W}S3qc}K5+Vp)(lm`W`(vUa6Ikcxmi0t zpxa-A%b`Cyk*i-6E9(|ijl_`M3RE*e4d3A9bgV&sDVKM_UlMz{+wr=Lp5(f6SwSgq z(}NpbyY~We0{|n_X~H-U-`lSJeX8f(u<5YrE+7JELGnRz&1%igO~MkHJSGU*rm1g1 zum$4>W`M1m-<8KK5V{2jGvMKHF2cBohcp_WX1!K>HU>6gJhQq)zM5luPS_(C_c*Ks zG)P~q5QV7!kPfs6i>Fqn+9zzq%c;(gwvcG_X15;kO2Q18<-2%cvMShA)qsUeElk}( zkr)ML&;hRRgm44zEOj)wBQW3wtHrZmkG!iUa^*w-mo-TxMT%Nf8 z;fl)W3xtQKXU4nX-rhaES-s~2s)hjq9pOX0{Y7=@b;UigYilgFKmY*F>}j&g^h=;b zMU6idwu0yle^Wt(1k5XV7h&`vCj@vcCD1EVGq#ku-);JUlwa07mEW7WM%b8;*$Rc`Fcu~1bn{r)<>Y(4B z!h33Is=m3#KGVK6aH)d z2535$j7$goHxZHFG_-^m@-~|ROGv-}BI5hYNy=ay)`9Umxy%VwhRcI1hn>yudng|G zuGv$;3AXa{Qy0x~5>#y}qjRiWq%~bt?!hi>$%&o;?rpva--+xTl=lQ2v_IeF8=;xt zT3WiF*aKW50Ae888gWsm#LlyVYz>49Aiy3i)WW&`8WS~`n3z6``1jX_tq>`A9;s0e zroxkn%@o{cxU3w<_eS@-HT9S0+F$KS^stchEOUh8C}!=Eb_Ax~B>2evel@u`*|T8s zk-HOxVi+O5-})ndK0za)#I};=p9!@8pM=q#f%IVgYvLogZD??Lryh^|YPc=4??SB93N_OLOc%)}boek?zO#O6I|>@E{h z$<0QR%2R2_CvOW*JZVqgT+VE68(9&et|tvigYTc~wp$3pPGAU*is@Bo=&QO29=&H# zO~t5$BqCU3J0Fh=E4yu^3?sZo#4Rj6Z$Q$Cz#i3md!VZGmrDm|(-7xh^G#S5W~c>| zaflxM_JOU##LUbeC&QGcjx{lzdna#lS^Y%E;HX&I^A=omaez5TXgg*84aV5Z-8t1C zTbrRR$g_H1O7PWJ3!dTfZ}EmEJD9ybR?dgF4>PTtE0P#+P*6BgILJpcveMuq%op!z zGEVYL<1fm%6tlHcks1>sYgrpo=ee1yW#i)usRX*M7#$e5EwIp+Xe;MRK}Htp7hqr@ zF_MPTLM7*(6$d6xCN{`;{%hI_aN*q|QoR~dD&oF?AJ7u<@gtx4&rfdet{B)I3ZZIX~juohncXFGcJ(utmeM=;vs7y|I| zYECLNfne4Hnwa73eF;`IDAztDVLf7s;?VxK4JjqiC9VMfa;!8HRM<<+3AHtlQP7Hd zXymE5y?HZiEEewWwTgsL@zh9Xoa@kXkx%n3_7eO3jV?L80%@)Qqx6}XjagsIn`Pf+ zf7RxX{=XJX_x8RVmp?eQ@wL6Fpx({SX+j3@R#5y8zt7FV-obeOmv977??S#{T4B`l z>FtK44);xLa+J&ca4!AM@MiT+p|LVln4x1BD8WBTjOTLoe~~lKcP5Qc`}b1s!r^@# zL<1BWga7tSdXvCO3YeA<;z8!P4f#5#1E^89kkEyRo|r)w9r#iW(8&j4NbDSdi9t%k)=rNkN&#tlZ4U_p2;s<-d$DF3^aukgG zRs3D?ixl1w6Z9yC2T8~n=V6(9;D+ID;CIdlfjcj1R19O|o;w^&MF#f!CLGEhX1iwJ zp}H=nf{f{m>ZB_j9S=XMw5DkTAuDH$Y}N~OI~~MS;+%SA#xjP6bD%vzpS4uzY6Ykh&|HRDCXhMtA-maGK=VlwcCiL-A<&KLT()$@#ZkdO zEI`!|nh5y6468|x)il=v2fzo$eM9+U|keb)`!~t8von}!|mkmFPyYyDI zf`p6NiZkI<(I=>FYp@xt&?pC>?Qhue}1}hyl}Yyg@UOFD46*odwv9Jh;mJx zw?-&&Yir=dKzo%+%=5Ga6#3x34_UeW@B{LZa1~aNM1PLak=VSH&2LfC9mbR5lUFpS z%58;L#GP!^X>4>X|2f>84HJt91rW|n$G~LHiov?>BqZB8fg+6n01~RJD)_H+Ti&B^ zP^cdYxjN5oe}Ert z2PH0}jujFZt(7C=qU+ zBF#c<6l@pJc>W9IQ2(sK4U`&Sq@fE2;CBMO>B;l`cznys^)=OZ zbGXNzv>5OY@*z7`Tt<1-x-IG2K_(a-H+bkepJr#w))n{s~c zp#E;B4f^i`SJ&_Yuho!CEY{NRTD+>~@~-vhG7hC2_9xF#7Ge6DEL42H_LqO>n*5{y z&Em)pk}K~tDUowE{|1SEFKurWPFGUPgRDJ5^|xK>nOQUOf(lL<)2g%`oC4Y0ir`hH zR6{%TDB4d~=BiGZ6h^QkkIwDXy&(lkrIzb*D_B#ZW0Y98r%7i9`b!InEGfvaQ3BS& zDIXKTz~bnXUyN54y5}4mI-pU$uAmL?3*HhG@dzkFbL(Xb)f)6;fIAophetq*1m}mq zz}dZH$N(xPHsIL+JT^W)4!Wy<3sKL|$Htr81+e}kq~Oy3{+2!N_6deS9Vw;X$K!rJoq_* zeuJNWItW~%5ou8{7`_5ETBS#b?$}F?o7TT83or6haO~nRP*`~Xh(=S3PKtt?<{;i| zsWf?tZ3g_Y4MJftD&fp+mmWI~yKH>kN-~r8Fyt(T5|sqY4l+D&Rp1w;5e#hw)^h^C zjVGjsKsos`o)nA3=BO2bqPPsLDghhoIPMnf$|!XeW}tu$JfVANGYs_7FJ)yYlx$Wc z%_`7?-U4AAv{!m?qymQsh2o)l4Wr8->QVE{-iZW-vQHE6PT(ZoBOCb+x)T@#mYHc# z`L`8{A3S2z4|9*|$B&&DE}-Ih+c}Eh?vrz?y`Q{TXB`kO8cg;^@nDK|G(L;GzUN#0 z;M%ZF`tfyz$tAp*XN0GpVjJo=2R2yYyL2;264OR++2a19nD}&M5SIKC>4XTy{&|HR zoqe6@kp>06FAt;srwOoMc&hMI3n+W=xrC`nQ`B$1I{l73T%Bri3mw6@VIR7k4PqyI>)v zof*;^(*kU4pLlG!%U`7t`GgtzNX8p3@hQ7K8^WK)eqZ21`a5pxIJ$U#{oblya@VD$ z2@2A%Uu$`jtA%4nY2#9;@K_td;mF5xkL6BdliXd4{99Td9Xyj8{CP#_Dw5@V2Bgps zVAaEG0`^_rdh_G}n%CTNRqRtXl0DMd&r0m4>ypi`evxmTD?dr5ZG>IUyG_8KL3coI z9{TYzXJ^=C7P+(C*$Os_%OT;0|CtJLyT#tj2Mx(5;c>kiEL}rbyCw-Z+c>Q?e zuArMyYuj-$VRBet=yM%hmF0S{)&M3KR1(TM?=2Z{4Z$sfoy-ypOjJfMJx4)!igUd} z!KVFLRJ>!L4q@ZNiuTCdPf#MQZI*Bt#eas?$@=$ShJdl3h68m%?3s^ksR?uz2KD=|oKoB426ijMLYBe?9PIoEh0mCuv@Kd8`%l zjgBTK_S(&Zn|?&I5<3!*bFFE^x&<^5J?wD#j(W%>h}=ei{0kafxB_Jzd`Ut`@iPxk z`z6SaS@?#QB>)P$h-8#am7(f*cOqh)e1A7e_R&YVp?^{dLnk6dIjnxxg-XbqXSsbo zj}+y-1(hDyrW(@;)?FJac?(kkx9a{*gOdahaGyAFkWN6p1Q|Q5?Jii-O7L}*zW{sI zduv&!7HpuAd}FYk-VsM(L8pG2{tvV(hTBN4h)5pB``y%`I#R$ib1EP*@=GVGi``WZ zF(&pZTc3UJn0PVW z<%a_7w0N$^SF$4g;z<5B@yFP?*b@N?FC4Ny2c-Xp>{i&?yluY&^t*&9g*J93;dD}Z zEB@TE$s0Xy=NM`{v44LagtV&qcYmh4A8*wi#|@!C3wZpXo6Ffi2tD5tdMJCAIoleC^U!0VKtt_iOfDTE|Mo%b z`u_V%Q+lGisyFIW6RMC>CpSdeJQwerm8!?w#m|8 z@EAi#fqUcYwj&vHFf4?VmgPNic8wA!9{nD1!{{xLWPlrFesKOPh_^|=godO2hv5zy z$N+_d&`m|_D`%L!(Tl97vavEeIK~VGgx*9>rO3TSveJ{VC)|Xu9hI}oc}`~Mk=YSq zQsQ^wE2?vUdFEUcYJGn_B%9b};n}*4|Ci(CiqIu5x=c=E$?v!kI$w1wQq~@&o%qim zf%tBuNC&!T(2M{@`VElkIX&lfg2D;PDS!fZ3M;1*xQ(P=M9Ouq5)9{!Cf~)+=2!PZ ziKMxDJsA$13*T~Twl1$nkknmXkF%LK&)qHG_6-@n+=WB+;`^gZ{UrKrVy3IDfDnO z1mI&BzCw%Yhk`ER_7n8DleOwJsNYrzb!gMs~21!K^QMSXB@yJ?illfs8>*!Ja45uujV*HeUdpASZ<`6GpY`lYrB)gSLM zi`FHHn*n@gF(!wJ@vYLMJl4M8r#sw84k7yypGemk@-Zjq#nmO6=wDfIeO0~?xgZNe zXQ_~c@KAApHwOblI&6IotqM|HtOLEU7kMxb7ZiZ->Fe!<>=wkOaODo5lmRb}wFcUD zr$)9MLae?xS`~qo-f%J;B&^iZfaVQWTY|_+{&tGy>TsI$3fW3;0IBCLvcC&A038&| zGSg}W5|#ZMZP72uO44M1HZawAuLWG;B@Ch=^^*D=RhU^#)?RMLlK{!z%r`p#GBZK5 zFA)wpNU(N*4|pQ1te62|m9%hCg5r;}JMgP}jbMYqC|h9NadUG+4S}ZrPgHOOVA^E( zfy+!G!eF4$uTRZ=q}jt6gE4q)=}&DgHZNCQz@c%t)ks5ZI$mlAUTT=$ ziKf4Tfsw12!wo$|u*5aghl!o&xT!g`C`2~>B)nIFj-1ar#bcV7H|f$fH@*;Q2SDkd zG6hmMtau>Gl3)P?*#lH;RC9|JeA1Q zm+*ZB!((7M}<9;NpW4i5J9g`M{AA0B%8IOs*3*y^u7 z690V>9ncTmupbO!wfn5heXb9_Z4XYaVDkCf94xX}@5U5It85lr1DuroyLs&>$CZo` z;DsPSI;>o_u`#ihcH#)9268rd{{cM)wg&K#LH-Mz7we8Y@3^Yjxg;t7;*(9cA6nmRY3^}y z_Cl+)tLrvha8S@^H8vDV>N%39vNk{ec!}$NxSarvsTx4$JXD0J->1m~>jxD6<^|8e zYR<3Ox)KuwORu2;f#yAQ@s0oc!00BqCppo%$X_-cX~mCHsOtECY7*boF1_h^ilIHY z0nobAC1pAP=*yh8oXeC&IMM8%X0z{KRB1j>CI5qA|5Csk&d~Tn|$i4QVt?zK!yr_BjX#+e2HfxS~fr0+A7V!SSFYZ$PCXKCY7rO zD?M&J8UvC=j{B0-#+O0Hd!n}{PU%H#XuP$Ef728H%8Z-m%NCnT--#%`R&aGyq&ns@ zd&yv`9ecZunOQX{|x{3Jw$QRqOKNu3>1<=G~m`~U|iDfs`lvB~2hrgyj3tNbH z-10iq*4^LY^AB@i2%SgwlRb`i!8;B7`|N1IJ;?f81760%%F_v}3UD9#{F(j!lcnY5 z>sTZ(^BHm(blbsdJhDkYT+8jQ=Yg7)xGheo{cgsg{^x%3EhPSRFHs+LFtLfREec2@ zMI=|h+Gapu_?snX5;?@T=U9;5eR&K#zDm9d-Iul37=K5-*C{;6DbX_R$N%@~F5ILmPdcS{7FDwu+v4CC?@(465BR4l!i1iy87{cg62>2d&9q^W= zz2gbfqguKsZpq#Qi(R>YfGHR;vro9v#or4h6@2K_u7 zFR(Zeu!%q;XN6&kFE5Tx8t##AE5Gm*c3LA1Yomq~AHF$-0u^Nt6R;feX<`W1RoW5r zV)BeHm)+L0E}CGtQg~7rNTvb$4C}`0l0*W8z&l5uztTaL@_+B@wLF-Rs-bKM<9z>Ls$#hAS^)gp(rC`P^tkALv%upEaOCNCGeG@pC;H+ z$Jdr5Q74(%h$#0V^^iUgLUMfNXXhq|r3zX~xbnNa=y6ZT;W_ixpGU^pv-R0db(?4J z(iZQn>~IO20>%LPqi2eWDfeG>TAtZ|%LID{fD<5343f-vgX}Fi$ppa;_)(&I`cIeN zS9}FU+$*vyewk10U8kjXOzu6#SC0?GCDc8j-9{_CRTH^Yy;}VoO-}8<02(Yt=-n>4 za@E|2XXNe}Um5uF*}Um--5!30qp0CzM6-~rr9_Ag zJO46_IZp**6pZJszc_b~$Srj#HY(KsS257JfMS&=u?G^s%<5b1jJP!3=ZNZ=pnz%S zXzTTi>jNLn{>PLf9&!ao$E#Pv)MscRl!J145oY6otIsbeh&w3f7_cPfAZR^$qy_`( z)e2yljtNlTPG_b^1@iy+TfCOq(V@LqlOOkTvU4m*p(L*e9~haq*_Ey9@cY4RFAh2m zPw3uCI&p9!%&^y3S=Yb=(U^+@5+rDXSK-n2u4iss5AxgGnfx;9P+Z)@f6Y?z%$*+B zo&*>~FTkQ>Fk68dthm6;KoZE7InOz^_;Kbas01B>3Vb8E^8z2J z!1qvtT90kvw?pOyVG9J8{1Yu;81n@te?mW@MOG9}DGhJStqwRg3+o5o^1IqJ61$c|&K8R~ zm^c9j=WyQt`|WcTaG7aWW~~Y9; zUsoRtH{_Z208Dg3-qN?fEwwuY4Qr<0lL?9mb^;9ndizG1uqdqD0wx1o@3hfNaBHwz zfmh`w>=CzvU+hWX;3tgsp#>Lc4l=P6xLOP|zV}TmL#bv$TC>LXm_4~fW8FYuK1gl8E|`$fxTHGVOY6xu67~yD)_wp>^3QW@(mtK$?wQ} zd~bZN7NFQO#7$4?Lgw2;J)H#5?(ip>>!li#gC>aqx2)-T{N7mv4Ov_>C$c7IXFB3H2w>g4c&|O z@W(ua0|WiHw{3|?%PhJ%2~ZlC*aCzBjU`(ev@>SUj0vJnnajP8dodV6l-oz!D>CX&C@wEe}xj0jYW06UrY<6b$#| zhcgVa8hNIu$)8Rv+wftXbyYTjJ=DYhQ z8SKxyWIrmX%g-!TZ9GY( ziM+n+b3t7Oj+p2&mKL9Y;PXM4KyH)IYEK=g3l|r`x?R^LfGb`e(3Q~OV1Ozg24w(G z*uF^i=~Dq%eXup5ak1NV4`~3y10YMN7Z<{`dJq~fry}42a0pJ2PfdhDcZzO*dLCp# zD+9_FW+D!>l|D2DbL%HGCC)=4V#NCscPdZ%V)~Nvld2~iT!KtHnTi$azSjAx6WM@& zl9PD|sRS|X7qFax512hcfeD|5%UFDgHz^PbC?)$p$wYy-JIGx@zgnvCXLa@E=-H$l zjSJr{Oc?{CpZf~nSr6V55rpm@qGE8k!(>nqktB~xV##UbaMv3!d2_nTA!q3yL*MP> z<;;caHB(S2>1t|%N8cmU8mcQTC9rE1QbgXsAVU~#hjs{j&=tq`8qoxM3b2Vm=l}Xm zmq?|6j54>r{tq65=gtpuZBBBO((~sATomYYhuz2H|JN79c9UL9^uNSn}*Z>3Pf0CvWJ?|>O=T+dQ%tw@!2z1_TYpd{GQTX9i;XxMkqW236qxpYbSm6e>I=#hNcskfs+>o>~v& zco&@GfVPf@63-mqvzgZ3YXH>YD_FD-dx$|F@mja;FI`wp)($o~uG#E+S{1GC3K#k3 zm^_%xrMCG6mS&JX0kOv!kVRm`04z)@1-5BmHU_?!gW%)}`t;qe{RglwV6N!={2-jI z%gvC3Lt+At9)bS>%nr^8WD?=vB8~P$0F26dO7qUO=Nbo;G}hfIn6HCdxz9sPzL}GdzPRF*LOvmN%Tcdj|*L@L4@>r3(&z0HKQ= zFGN912KS2SXi}kMQRDV-nJ9$mEiAL)0OR%k^cJ9Q5}?3n1Evnp3m1SV5cq1O2sv3m zzylvjSkQ2fiT7XOMRn8yOMZ_?5Io^`v1{P^ZN|%_(v>73dc|?yh8eFRGfb=SJfOKf z25eYxD6;s*hU><}sB;{!5ayHN13C&<<Atpa7VuKD;nF`~!C5|K>+%gZbB~MrdH5jI8V$WCegvrhdC~sp?+35(N#- z>Dolg{s=LB7sZ?4WyyQSyV$W(%`A&vHfA2KsF6t zGPW9_mAO?Q%6IMOq<@n>l)%l4%3gIro0pD~QpSlx$p|q> zqf~TF61XK4+0+C5^ZovVm8^{zec7~X&QYKV!FQuQ{J~l{xRxywf;N1O-`fYtc=-5W z1ndBbb9c8qZP+N_+%Q<80f1VS(aUv^OHk#3$qodWLXFZ()4d0m063urgTXj%Bhll9 z>wwo&WokQb+96@{Z4X0Z@5{B6lz>M-$FBuqcHgyX?f3Fb=!PlS2j#!WgY04oZUR<$ zbJJU07}NvMHJ}(Q!op~ec^KCTE5IeR_(~N%d~Y#a!%a~SH1ia^KeDRH)ITY)NQcr; z($dx&v|zh_!IfmVMC?-Kf@XJrLfT|*=nM}{eyyo@=hvxZks}?>dcKOD=IOGDcGFW~ z-54#Nfn-p3y^El;r$<3S;i2m`{kR_j{;)uHh9tO-6~6Ue&6FtY8`w>$o+o*490cZu z8sNZIh~x6=F|nDb=xJ&30irVm4_mB;-kKC9dEt8^#h=&^pU)CgPE$3j)#Y5};?#pY z{?O|i2YMPXG{Bw;8pWe^?k=vL2dyxQ9Vor|srA*?;}rh;`S+j8O@8u}^J8mKJclmus$C?*A}2POok z(zY)_(hG+uWV(hwg222GJbih7QL68|ouT>obfWOm#?VYiaLUmk!K(9T)f~QY9s}xf zWbdNrle+B2PiKGU*B@U0qWA?3&pf24NE${@)I+vfr+WdcO1M2@=^&w3r5Y9BYY+EU z3BD|17sp`@4el4aJSy<@7wz5M=#os8br@b0r0fG@WnBUCL&#B4SE0NA;DLIv?pru1 zp+^dbAZ92piuVrD_lTa}`pyP7KNrU2SZk@zy3WO*jp0z>xGsN1p<6+_3aP;iore(i z&~fEryk={gCI8^+Z`01h$ewKGuRKpz7^9Uq8KsplabFbOdP*FT?VZtlaLd+QmOIEh zP3#7#q#Nm~l(^(-y5C1%-*AO-<8`aVVQaD5Vo~Ys0&M?>wzrO|a_!;(u?dAE z2qGmZU5bKIibW_O(h_1KBBhebYaBu(q@+O+1SzH4AVo?_L`qtaZWLjD=YDHu&8(S! zhjo|V^}C02-t)fCv!A{9FSfGVF~?62`%)eDCziUq9j;Kk<-$gc>`lqFMZS&qV$6upx7fpgF~hd_&M@0X{Z6y} zUi{5^9H{trH7%{)cNWLn7DXHjO+dQq$hxY5!v#0T#KSG)B7O&OeFVIvo=UzqynlK_ zz~*N5d!RQ%Of$9P(%()#SL6G{WHI^9(%??cfg?YdySthceEeNM|GcY3SH_aYV7Pafo;`K1 z{8zbmvz7%wInhx7fZ~$cb_)R?kKMv(5qdkfviU>y{T1j(2U~K+Y!Jnn^83K*a&qZO!spgCyZIUOP@cO-^zt> zYyBTIXbs;e0Brxufy@W0ln$6`{ZH(GXLuKu%zmd1U}gl)Uzq%U?L@(-G4ym!vQ~mH z4>qX{x@H<|CQ90E=N;_Oj4zpX;LtZ__TRpK_Enpu1MC<+g`7H1pUiNPx}5a-lHQxd zH|0CZr(DwW5)!V!(iiD8PPJ2QrAepr?qu(K4=d>ZaS2A7LBdgXaXn5=v1|p)zpZ<8 z2M+)o8I0U{asoOg9<2Yn;Bu`0n<8MYYg;l?1^zBq!3g+N;HDFx zrf$XoTezxTy_U`TqrM?m|LD<=csa#cHzHyMhJ{$7eR%dD2pmH`k2wKYR4KaO;wSfS z^dCTSGmGbqbMqJQm`j<<=Iejjvxuv;Md#r&BE{I-yc%lCHZPeDs1{KtsOZ}QlGrLf4sGzyJvhVx-YLi)f@lfM*cPJNAR!o(SGW)lh*4z`vD7Lk=HAkYDlZQW0 z6&E!P19|i?<}tKG9e#BeHB&PCD(pGpWNpn*HzKovOPkq&ZCmuH-6z^^QV6HW+tF5_ z^k*ov3{-62v#vfh6th9Wi@Qbn2f41K|JSqUpg#nxA@77z0ldGc`fp&(Ai9_S86#4W z-~_6sWHB*NEqM6`)5P>C-1H^SM9v#Oml4&73>B|YN;_i0E7G?!fJA$7qk=^P)9A)< zs!~;R_c*btEY{*<#eISkT8-gxdy@9<9`!jveVD@^mGy^(Zx_%CtLphlsV>tD?a_mR z`7>CPeyq|PdH8)rs`5KFavSS?N|g`^f70hA;fk6cG+7X~lDC$q5EuzyK(yvD=R zlfvmkuj5(Iy`2fN59~OmK&%JI@AUK6e@FODo{uf^Pok?&lK znEvQdX!FDt+sxzbO8+XI`|dlJIQ^Qac@7IYU1}&^D!;g0#WJ7ho#c$<>`w2l4)c}e zl*YY-dbI7kTL(fknnGb>M$O2=fls-Ej%l2}7;!yzXY4&SPVT?jEN6bub_28RfR{?<8hihAYmU--G=8F+emVaCG^8MY4}(C2|6QuJf^b4^!g zv=LTAF0mtslw%HsaO8pX9wXXFgV7G^OQSVKU?M`Q0#+}-xb_B6+re}ajR-2i$3N=sU zYxsOF!vH$ZwwH@X29FLK;t0V(-?$Y|-`CswDD}cYUMdz_b0d+|%!6pH({KvyP`6FHI}8dQtXtHz5gJ6pNXT0TWkH zZ5X?6(wc+8-f`+elMGzMp6#9+Hf;^*1&8DJ!{Q&Pxp01h@95~X2YUhgk=cfp19c4o ziUQ$*;t@n~j9!73_5q|AUMC6C z|FHbQZmmo)(VXP}ISk4)(VVrRhO4uOMY!Eu32c6hN4euS{47RB;q;c@dE;@`$I^^u z%|CjMe0u)1r)O=sdBDqY?^tfSLH#b+s(X_5vu+qdt6u?aln z{BIwLQF*rI{T3t^8#f<=#}-?$+(xSD44tf9$5{iM9|<~F+-6O>))r$O!)Q&zY0Vi~ z6zO^@sKx8|hYu@?XIl==ZHuy#{e>z9h~wHb$(@AF?(ZmYJZfHX6{Lby2IO24l0{0> zI-r0&;0(TrnOP5oQi@QI&8+)Njd%NgpvC9iF9-3n%UDi7^U!-49nIlCaL=+p=;t}N z>lK^8m6s2FCZN#&xcgIMWZXaPrM|`Ao$Cdwr+?Jv?7Yg?A8kLhZNudEOulkL3K>*t zM454(wQz6^g&PHQAI;#XdJ6V~X%W0+R?lP9u!gc07L(fEl4f)uQD=oBwnd)<`QxsW z=yA9Lgwrzm9@o!qLDk~A*m0e}OK||vp2lnix*a>ZQM5~?P^%;t z6okhULVZ>AFID?~|D#PU3;2yJX>MuB7P&Ct{nn$JL2Mwp;Hk5pe0d@FcycdRi+_?E`B7y1e>ev zx0h2hK>j1Z@)YT-YDBch~sea1s2 zsUQagy@m0~V62B;+x3$z^GI5t!I^wdv@^H5OSNyLqIzI;2R%j{mheK(soKYFhU5h^ z!Qh?;;1Q=48|DN=Mv9z}Ab$-JK98gvb4rC3qLBt51;d|WGWL~KRhkj|6Y<$goqC`P zW1yzTbqejTCFgocJ}oG``*Pa$_*DUI8yp$oBS*^IAhJMzz(44~jp3SLnTR2KxRscH z0K(M5{4+XwdL_G`R4Tu)g$q%95Qu~54Wzwyzx%WA(-|W?AS=q;F3PAI+ykej$&F_A zNJ)^XE&%zOf&i`5l?Qqb30DEFeD^eSKZlk9j3XBel?Rv8PM>>%Uid4Xpf%%1V&w&Pas06?04z48NAP z!U*y?0tP0?4I}1brzsX#8w9g@uE50^Rb@OR>I@`&(ah_%U%PX z)V3m|2~+R?g2s3zV*gO#NNl-OUFXS%e;8!LuBgKlCk`*{jEiNrlCc%N;LwoMK!BJNPou9!%K0lC-;EdWA_8PE~2Kw##{hbB${XH3U3iSU)i!qF=rwlZpn5!$ghP)od z@rurhytH&CR2y<;THDUp6?cy`#wnvw^64?L4dNCVHq41t3=RrHDM0UcOKHb{5BW&t zkOQ*AacnPiHi|!8oSvJ5n)EWo`N3(DW6vI=5!=KoMvJ)5h$d(jEu&W%t$7{vG|Jq~ z`O)BK&$MkHTXSxr+}EQq7$B35KH_hRKpzVSD4ot5U&7B1Cf&#i>gDhJBJJGSH?cAG z6mnQfoAuf-+LqwoV_sB_@hue<74U?R-{GJxG38Fa^xr;dz0XmNJ4Rs(l_6Y7n$^|U z54k9Tn214^;3nc{-@bh!B3}e;iqJHhK;*uB^UOR-O5?!;eh@5W` zh42}aa~p(czQG3MYZcYi<<XOz|N14vq>ElQ#;`tsl8W#y}hl>YXR zn3H()io7K{w?BGrYRk5&Vwwg|=WlZkk6w`)yQg>pp2(Rvj?CDx;2d;5Q`e`%73L5g zUOuF7%7GkoTJChp;WJ+tMOX~%6Cwq4#WDs}4?aybRMDH*yN&9F1dJu&GM1KhhFTAN z!r%;Y$q}IR3il7mLT&csnky+8tC6+^A_OKPgu>#f~V7Uza)CA|r0{ z@FWU7`ux9(E@}7VqfI0P)i8gZ=B$%aQw#m_5mno}q=p~-8P;!pdwPF+Vj1(@RLEhs z&o32UCqAAwYsx~GNCv!!uq2l%?VRG4(Hk>Nn<@`2;eZd6|O$|1k zFPb*z!Y^ zhQlNku{ZYWmrR;1lVrXLJaPEa|C1I77nS+u(R#rrzf?T_`F1_$$&Yznl(n;QCj7gA zpQC!b@5A!wAj>crPnOf%y4A$C%(Mpr|BRY@g`cLri@l@^6vWQTh~ua@^bNUX0bS) zlrJvH)%X^|B1@j0nLaUj>5by~Njb^62GU$XWQGArLF{!(5 z8@H=+8niv%WHq(2aU(a>NUraR*IvI22{Jx{%$@;AQ zSMqL)j4>n*CD#SkJX^a~pH1vr-{qC&k*`5XTCX2-ox8=_-}<`rN7e#u=N{v(kMS-) z(li}DUuetcvd(=N{ytH9V)}q}*jE>c8+UGewG7^uxGE)tZ*Vm9!TfNxv*zV4F5TJO z@P_<90(&-k>U@zP{jk~;@vkJ$I=E_Hl#fx=<*B=A*U-T~axha=R&aQmx6S*? z{;&|{Ii>&&-rH5)U6nl&&`2Nsw{f)oW|?jM&Xz5*@domVXOU+)U-1=MKGoXJAz>T7 zbjZ`tHVpUm1=qv*VNQ(ftD66v?m1^^f84fxBNvm#a<_E}p*vjAZ=OfV!lL zoIcv&8kLo;w;*Y|dtmq`C`;*2F}HlXS%TE%#UAN6v|?3I2R!nJV*%ENG=~7eqflG~;NB*N@Rzy3NsN5ARWKk$5ESQcRk*r2fXCFSR~( z`krB5^+SQ1b&>BCcuR~}aH)SbH%B?KZX`Ts3ixxD58%6Uowe*^jLzn})FjIuU{T&U(Dm(lLFP?$)^U_Gi< z*{@UeH^tnwLbv+)e~znROflX+&F*E-(sfB7jirClfX-s)w*slKO-^!lUhKr}^P4L# zZudAZqbV;fsPfsf)6iG^!(Q5?FH=TUH780w&ykLY`{?nIPn4)u)sU~ZMMn;*9Tp9n zW~323ciW0NRVO?DgDY21iIcmfbSyrvk}tljj+V6%o4M@H-ceqqd36h_2Psz%MIq6_ z4+Q*ZSAZx0F@ung15wzZ*VPc%GK6?$8QD@??1?YpURaKzz{t__Q;+`s&ZwRFBgbUE7LFP;P;qtyeX#`St&0X3fd7N5J?g^O)3n5Whei2CJMbwiKMH9Q%su~(zdlv_ROI<~?3=mUA~ zXEd$*e_Y&WSZlc{;v>n)JAAjn%4shWt;CA@phUq)f+6dr+^bO>SoQw2lxz$lTDdPfoVZrFfK1S7B#bwEY2lk2j{uN}%V6oZ!kZ95u(ls1`W z5>xp*ki-P|DL-K0N%QkpbWUw=Jc8RlROX=ehW#V_#P8?8sy{ir2qqN%de`Kd2+b${Y~Q?;i*Z zB7ScoVT-mCQhMVYOR)FidY>+~0n2&YBoV$L6OCN?v_FE9Sqc;ogl zCW&f!b8yW(M%kdJ0AT4Y`p;U53%YO}0wMAwNsm}G|32h^9>yO^n*VPx%IvKzj6YDd z{$2Pj7^4x_zRGeK(j)TV%L+dLq&r} zPHI#02&DQZ@ZtJS6!Eebv&p_@_bM*;cz<;vdXv+y6Gc(|QH4QW3Tiz&YW&!P*j0#x zV7nkdE+q%vN4zpRa6S84!!_DTuE&#BwTH;N>z2B%f4MB@>C6?Ve^>I=I+c2xU-b#6 z542R;bj&O)?kFbF^93$KtgoTq0o5z@8=nJ*6JRh@P7}Q!%)!ZH`N&!c2BMyz_W>k@}{ptnclrO8Ndy93pl(&m44`(`{WB z4oBanmbIK?)qGPFwfgr$o8sX#&0O=1+WBDHi<`fPwZEc}kmWe>Jrnij{)n`FbkH85 z)A0wgMZ@QqZ8r!|=>;dfGY$)vOGlJnSe|<@`t?siMx4wm$uAN|?2|m(a_;0<4_mTK z`3BssI(X+`g+)%7NFtUNs}mka{P?9O$>hNr=j*Ha)pazLvM%J#keIqPVU`d4EC#N{ z-$l5)_Kg#T_bb#5T`G#D!swi4yz?3L$)v}u@)o=ElI+wAIJ7S3Y`RS?>S4e`HYup} zC?u&UR9&#Yxw5H)M*i_+LBB~K{Z;Za&in)4IXiW^cP(C-Vii6t&|t9mVe9FFpwfXT z(Jm%a_p=_wNjp`nG{t3VeOxj_g7v=#JUai(`vmoq!$f9C7%y3h>%4tFdu3=EXq~FL}d-`$}ZYUh+VN~RfEimF9*aY!`EyE1XI-5 zXg*=)a(=$ZhFsbNS@WT)uIoeB*R|JXlExZEn?<`doQrcTj^`bhcPj=i9GLOhYwRVl zCB<7ap;r6K#a4eM7eNDAdmHPz+UY9OHyNhsrt%#}6l->n0zRo2auhtSvJ|yoW(<9X z#=+&vbg?R9d(p?zj$IO)g?SFpjDAruMAx)S?ecv0<3ioC1MlcW!i=e7r%lWk2Vc$# zR|j9)tYaoJJXt_zSf)Yd80b?w6#6WiaG0f;3?#x=M{2UT=P0OP zTPEmBx+MM0vy&Ve`#{t;?=gw-{aFU1ypapsBZK@}u1eP}esLTY+^uA`C5hX)EMSgF zpVnS`xuP}Ac=;E>4f%ey^$yPCP$}K zN$l5^E3*~k=X!Ja;2(Lve=R-b!nm>I$F!T?Kd|E6e3R$9i%tw_nIpX1uUN4@t}I-> zj9Ol0QxdzA{5!%E<~Is{O`2V!YCcQ+tIk4l9zN%3YSs0HM+VD26Hncv^c zPuuv_FiENa{S73h&P)liz1sXDKi>9yXt%ZXR5<=ct@C^5e9q$dtqIO!r=5#swD<7c zP`_$0o>&K{X#bz2S=%%Sx)h0yi zE_VwLB{*ni47ciNiVP))XbN1u>R=`@a79F4U?_pnEdG+-X5Nsr*-24XD&3#?1o-BZ$nP;?fPBM+Uicc-Xgy%!fz#%ysgb#d@8GAa9YF>3RF ztjar5o8GbY9dQVKH?Y`HFV9C-jUAMl+|zR4?QipdJ16zmD18#duzTn|p$C_7LPtaL zIJ;jJ0}Si1HzDx&)tUb`zjlM!rqOi$1B_J@krQsKizd&^wX?_gHFV5x_cr7;Y-Zp) zyUa>(&5R6rLki{19ANjye|q}X)QGJo_5Z+rK&IOPGtZDq<@P;9f*hNY6G;z!nMi*uRlb^FfD z@;}W4#Mmq3!q>BtSX$($t_M7Yp3~lY#PlbM#A@|bvOY((IA#rl-$-$O@ZQN+VoE=a zo?33J%gV}*A3N|3yyeaX9ene)(3Us!SGroa52|gw>}n|JGJ5>~Bonxu5s7FmYl>ez4<33KI%Yuv8uxz1 zKEMNz5Eq|-nF+`KE8eIO%&11w9WA664eHj2`42x;sWXsd}p{aDN zh@FD_gR0zm!o{u>Xy$3!GMBdfXkz1;2PcEF765@j+LDa(TgiA(TPHltSBXITtoHf0I}UD7 zm#W%K^}<1-OHchdEmJ>paOL0C#hBo@RJ(Os7Zp#91V%Tm@j}b5@vv}Q{oA{qQS&JE z3up~a5QLzc2Wr1-F^l#q?$S2e)1jx8cl?_Nl_C4}b-~axR!3#09Z$Y^y+>`J{h>Kw zyZt-s$IWNNBIG{GQBC%5{yB5TR_cD(HG`E*<4I4RGpx}9gO;D4KM;3MGe4_m5X$Na zNzJZmd*#*X$Eur&g*Lu`$etHBHJM*gkA2v=9`p5z%4W_?ft$5u7wCA3+O*bu*WFG| zO%&w4O3nS9Hf{7))}_=?cYGqAYFbTJK$e*Dw_z8rj& zYQQO3GD$56+v)R{FVG*vm6*~cx4oE17l{dt`}gA0wnJTuFDJ|SHG;6!aw|`ERIbY` z2gJ61IKSFJ+H^;4>(8cnpWk|?p7W5YV}DA=9*>R=&dyb$6 z!7O)UjGRW(7ehgjJ@&f(7Jrm{a^PKoRJ+el=G4df4EyOL-$c<|uArKJX6SpXH(oJd zxhezR{A4`(^ojJTSa)I=Uaf@)ooI8DEtGmsK8#Pk9_w-qwo*8smK?=jczz5$KV`_A zV|DI({F{^K4F=Pbm78vX=>tz8_eSuHaz#cg*0?k7d4&oI3*+ImV()_VpltW^BL$z` z-JVe!!f3WX*Lf@`O2fTv^~ixs`vSQ7-tMCsJFu0wtUr`-+uvpYYug0DePVXIJ>$24xZ->Q6EAY{Q>4z{;6rHdy^ZvOZ6Tiveh1cP9*=eyEAHO z+pcpPBPPLvHLuwULIH@P7okH`v4nRzrSZ))`Hm4t_=sEv4ik~kF-Vox2uNkh%lEn{ zRj@s?R$Eh;n(KBi4)YM3a}=j)b$oh!H^DO7adxI&LO1No$moyE%U*_J?&CZvv4ex> z&u(SlN^2Q~Qgw7f<*4|U(FrwXZ@OWRMz_TI&_CbeZlJLa=4{HnC`>AYWj$Hj#Dv3N z`KQ-}g`=}F7}f6`2tuDXRPMa&T;~g0Es>ukAOX=)=;em`1%AOeE5`o@+qv{t(4ci-C9G;Q-(sQ{BO(D=;W5f_g^b_+}f5(osY3xUCs@>OO(UQNWgTuq_ zvo(U?sbf|TFc_BrE=2$m+3+?be+6w>WSqp4gG^_)8R7gA=pRyW5T%Q+3s8uBzyNTm zkmHcGUKA7*yn8o|CA!l7VXX~ZZct9l-rwsuUyds?cO6Y6lgXg3Wn9Q_$X=VgtQU8z z>R5MLw-U|21ml2>M`KX*UD#O?Z{FfAoGIM9_5KC*9(&h57bTcSoQ2Jgd0^fRn<%SNR1P z2yBvPwL?w@&iQB>q#I>d!B+zH7^=%wP&Y81TnXF6nI4 zB51+TQ_Q^zPy%y!x5}^B>Xa&5L6w1p0Tf4Ofo5;My+S{gnn}DXq|1b*Bu&dCZs$3^ zZ(Gw2GM*wo!wf7oKad^+@`({z!8i&^hxG+wi~FCN-rl`DGT;h=`438&GhpPH3!;^k#M67GFtZ}EuJF?M~ z7@V3?zFf84f8aeJ=56}CP-0=VqtyfAAg+<9X>2@A-6T_r`wpZY+#oaF!0qgZz^-ZR zv+J5N3|OjnRZSmH&+%oEh}}4?>Rge{TFbWFHxZ+dM{!%_!1O&BfLSRRY}Num2k2+U(hyta7hq@3AqVTH2hQpd6@^rAw5lts1Rc zDf*n?MavBw?>Z0|mzy;tOHsC${h?JXg+Iyl!!j~Ba(SP5Hc?- ztCZ`4u)19VS2}>>@cw(tc7CTGldPH(YWVb=W&5`O7S{WfR3^ooBv}H$@6$zi79L#* zEsJ{@YQX!wfUxIN`30973WE^L9*=S$ew1mS5aN6Xa5xM$7xhbu(XXYA>z?i{Szt0m zVMftTLO2RZ5ym;+1vMkIceCBntvQUmu*okDO1g7|Q|0sM9oKK$wr$f$)G0M18VwZ9 zJQzxxjzLXGQbDmyvT;pbP47+zV-?aQ*HWc}2SfR5Kn^0f$z&EKi$(^QG^>+4b(PP5 z;1~5B+y1fiU-G}1oc{TLv6VX;_+kfZ=(ljLxemMy95|)Uu7B(flP}O^VAQ~Lp%`mM z?P!p0ERgo0cP0CXCf}i`9p&$;-!pG&1(X`A8qMc_dUa1_ghZ=iMz7vWuz#iyKtNpW zbq6;FMHD2E=qBo|Xo&ch1I$#+m+P8)^XwUktgWf3xss$BWc~H$%WHpJeQ8B`R?`b{ zd{%#d(rvRB;$}qa=aT0B1Iwc=`HgF3$wN3{DZcl5oWC@8(H&e|}bP?evP&vAudYi(?8)VFjK`6t{-Voxc0(hZRm2p%OM7$CUAYAA6w zZbp65Wi8FW#T#Z)=qNeJDkfWeotk(;E-t^&d10M(`f@H8&~lJS)OiDaVeo)W3>pUR z&vEKN{wE|xo5o#-j#Jafs_~F5BU!$2lX2)l3+cKzLgDEjX4?Z-o+bs$CM4}w8cb+1cda6mP-1||)n^&)@e zGv}*g-E*r(oJoNoih`4;Si^Ocb1M>(vtkj>s&{|Mw%NA1h9HK4t^Xum$S2-87E6Fn zC7G}&_oRM_IDdr0AAiz5ya9FtqW($h8HIHia7NS*qX7#ZK)!WHbIaXW-vxdnHzGB`9|h~|%$a)&Z3b9r_Z#syb&#lT z<#&1{;bKTCAVs_ovcf9E;e1z)qobn()KAy~D>Tl*VPPCB82r75SpNq%Q1HAPFYEO> z&7lA(`gr&4sCn>o(3m_e)!oOWAH1eSpe@UX!lkjEdZQ?F3Ae)!Xr`^X8K0m5ZU5l| z>)uly*%9xua21)Ena1f9QuC1P`;q_l7(V+}dq#Kg_=Z-C3dH)o?l-eL##f>?QKOLX zIdPJb#3{asHGXYn9P`QE56S`p z9^BMPEr-DjK@L8QJqoe}i28A`?m$TeR!TPHq=hE_q$7|b{fqU%lxsgfN_fYB^Gu|N zNcXVlg7^=+&RbfXd4*rakqK8gW*(f6_UOujwoP%+!9s8{SVl?no>&=ll;ZXhnif1e*Od?EFNy#0|Td-70ii6-( z#=s$`%^Efd-sQ?dyE&yG39DTI*M!z70oD!((XZtE=?0|Uw2h6ATl){-xF(eT{Cb6K zgqJ+&k~X%i&rAODIcw%@m8|2zSR=XYGHYe32eXTRCUOTBch{P&f=O5r^kiauLbt2* z@6^c95T#L@7-r^L5|Q;KuKvsQgJHF8$lqr9YV;h|(tfmfS6)I>p$6v}fAp)PF@k}r zuM>i-~acI+w7+ zE!F7!`x&Um@)@)E!)b;|UOZ{D%6Fk-RYHCN!D$Zs8))fZ>RJXKjK|-uEe9Rfpi;b# zL;zf%TBpKIcUkLnrnWGe&ARlAoNGvKO>T{ej3Lmn;vviV7xxMs3su;X(uTGG5+#!7 z+Ems0u{)c7K715$k^GFwY0-O1`hCr^-lx@2+L+2uQKR zQ&Z+jMK2?d5VHyt9x(VxZ1L;`dsS`qj@Jl(sQc^i!v5#{)|TEI%l=QKTrydG;t2?s zOeNZ}YD99<^=Ee_D@oMl%H5gy{y8(9BmLW88qHlV*J56(d!Et3*{pxPW&=WxiJIS> zHEtg^jl21$YuvkIbZzu-5C>PcQh*=MBrGqA5UQ-46BP?e2?Q02I6!o}g4yMfMlk3= z+ocGQ+MK9={{H!exWW8aW2L>$J3_4L{rUNZ1Fx@7vo%)hr|SI*+eh)&q?0U}{+M)h zVdebFNc_8#C$BWkmyK>ZyQ$Qao(enh`$)Dx$51;-;nQUWFF6{)8^c!5j|${u=U$g6YJfUQPmJ95q9(EN8%x{##rICqxYwi4K#U5^%O-d zOc{&#O@p~6eB56R`t>}wDskJ{dJWjbRi0W+5zV#QKW`cC?8VUooSsT)w7$xgONVn- zCR>kt`@a6FF4nT;)O*{`do{Mk>Fw7#E-nlA2g(HY&b)NbYKCn+TmJQwXjP^R@2cx~ zo-0lS`QJ-zy1*BI8f~FLsb5z50nxhA(XGzSpOXVQ`tbmaV)HrT*{K2^o-TPPBV?2) zW%K!89ssIGf+tFn&mN1SE0^4Lm8I=Upq?cnA`py7MYlsIWVH`lx}};$WYsZ$5_LED z$2CsRb%}NM(TvB@eP2P;8&m0Pb*x|9L2O{p66kyK|bdEu$qPN8p}x4uZms7HaHamIO_I;ek56x|3FiB9 z%2$~4w$tu>6!>t;p+2yD5K}l(l%J$H!DB=C{A+lh%_60Lrh1X~`5$V)Z$gaB6APWi zr%Oj3tv-q*^p?;WKXn%!y?=WM+pw)%b)KD?LRzaYR|lSrX=f(O{a_X!xWa2z!TW7a zzV4yCNQrt?jc&BM>RolsH&;R(G|>Rxulf4_YKjjTy}6`^2Km5Mb(XF7HtId$6_UT^ z^pl?5BD?Rqd4O@x* zAj^Vwivaj3dVYZgP*gnAD9#YYv?I|xjX$L%8g{n(jeQFp`|+)ZU|}1yVy3q@xMHxT zy=H&L<;?OmV2N&qZu1A1{Ol{cN)?}G#_prF_|#%)QJH?I>{?~IW7lLw%zv??{VxXq?6Ln3sX|j)Apm#{NgS6yx2c5%`yJvW}q;iv9UTfQ|1>9qLS52^krawt1 zOP!;SQFG&5>K}qu-sZYgm0uxe!Q;~Mvb>u29rG>sL3hX>47aiW>QOH3q_v_Lx z`*KMPER9oi%El_$d;FRlyWAT$y)nw2-n4II5LkqmsG&#D`5w+D@~NxLsVSLndYmf+ zb5vhQ8`q~)k5$vlZr|;mAUfhuh{+y>$X!sj%Jw)vCb+Fk|H_^`w~i(*bVvt~dVnRY zTys?LV>{STYMNaUJdMleXGSa*`mELRLV~O02cqO7^YinkRPWATe|ffnF1p~r`N#<2 zS_SAnRb0wWg#Ry-eUeu}7_ABTZZsbPwzPcrCXbkA*Ny6N$1=^n5GE%8s5|ndy@^Z` z3;lplG^6{QV!JT9+Z}dM)5Rv18}I#mXh){2=b_c)=}HqExI)cyF!dC5jdO%IQ>ghz z((sLuXEXbyB~MZF0F}ypr}IT^%1oFPe`V#MN}AY*;lTZg+JogJrRQE9K_}Dv-i4a; zk+~X=)7vvE843<3*c%(NI0U`3=CL8-_&LLYERso2GY_pUHryinofK2~>{rYgUY=cQ zC)ej@>@FC}q*C&zG^W*K>XBgY(`TwxUlK}m0V2M5I~YQ%Z(n>r-{itnIY(`=nI9Zh z6Wg^1zS1fmX2?PIi&-f``}fa+iB8d!roJhurU*VLTDc(y%!!dA(0u9~qLO?(KRkz8 zDONWcw=s&R@pJHCA9aAk$=uxhTQ_X?0AjYU>RRKZNnv}MDa$XLq59S&#I)|&b(26d z3+Y1@oXrhA8R@0B9*Mtvll|sxov0+4ipPxlQ(21xU&7|Cm&w;yMQw(pdpruG!-K1x zMc1@`)2n>e{Jw{%ctN`|Fr}*No;V_~aDIiHG8m$=E>0sEXve6_^Vea2pTB@sRI<-` z3x$1G#2Veno;o~!F8Z|fre*4f1>YKo&nxur{Y#kJ*i0hrLM!wzB0d@!uK)Bs$H&Jh z2_z;gf`g5N$@nN$UDP&U0^+Bd!+B~yYhStYnK&S!At`US?Gw|vjAXg%CSV{?{ycqP zN2<%T|Ho2oxx1#W|5E11^g7zW5HM;M+da%U6aTv5^{w$+aCgef&VOu_&ZW1aDrjdR zv3pYF!)ujn7T1tdSzf)2Jmi^hpJLngWLgmu+LGdj+N!#-Ua`wt?vmflV-zG@JHOfz>`HzXsVDn_UFHS* ziQ@RnCeme(rgtj~y(Np)1P*sG_I?Sva#bVC_K7We!Oj(;hp(m`$SZv1;w!rE%bvEf zrj11&8?*%ryC*4a(V;Iy=+?(Cq1Z*Udjj~$j`>3aa+9cokjz8JuX^Xs9V;sb9 zbQ`}sp4Sfu2}*2FY`-l~h(+K~SqLr%@41~6;ZIq2U*F`k)Df{RPA*L^a6UlAc{%y@ z2=T_FTu$+gB#C*K58Ykf7|jGG7TSs}Dhvr!`?#B#iH?f41e$LCX68KuJ~T=9tyQ(C zJ_f(ILCNFLqg23LBqW%?rSWqi$MW2ewExu(q;*hO+M<J`QR3$OUy^lIDSAv;-B z+l5FbCtB&r*UQ&l7QEBeO=cRsb16n@n$dsysM8B6#@ElAwjTIoe2V?^=B@i5t`WWj z71Q)|%%@WCGGV?OVged|37O$yEYCG+bU?v*I*%OW>tFSXrz%}MDr7&q)41(2_6MIN z@L5pNQ$6^J?jx?G7w!~DG(l0P(5n7t`YB`6wVWBuQJaODTOM)<%FKnBBUkrFz&a09 zdwB4WSXlIl+=a(Q&}kQV0dLdRH58H%V`K&wYHPFhspq3=r4MCT)%d*DA|C7EEYG9@ zvXAqLp|ZsAlW;|};TMgewCEEF*9~dOB#f$7n{r zhHjzq5+p#brSA(0?@T7A?X-UyFJ7d6>EJX+^Xi_k+?JZ)(Ctz5R8&1WP}S+{pE|ee zYzw_t`GlheKO-oEq3WUPSGBdD#hF*WDWY=L1r#{uyw_2ilbo99)F=+OkGooMG233V z)r#Hn2h=|x#0vZ;E&$h+hN1}|@PC!hSr9SuCaPvHS)Aj zpgZ{24E#~5y-?P0lS!Ty@DPUGm$@PxznZHGtBR_sE)iQ&nU&T#!SZ~k3P<&ff~2*P zQl`L=X(x;%HRyVeJ3p5>N(Q!&|wy+l+y5Tgn#uO>>*o6T;+|JNAo-iShEnJkJpBbZujH zawVq1c)w{mu(+k5QyLjbYO+kw5w6Vw`U;3?*GZSsDdUMxb0yC%Ex0QB572t3Kp*+{ zFIv%fO1O{nD|3z!cngZYY1qweVqW?}g;K`gQr@U$l3q2(0ukO8_G zr+>4QfwEe~{Ah5^<~QJwKhVN&YN!T5$qa6)oIh%(xH#KPL+%{Kt$puXG1IKjWO8I= zH~hHS*sKmkp?`-El%I;HLL~x1wybJmGpufNjq9_lG3ENx#Rs18B0MGr@Rt8(Nyy+m z;T*B+@(Z%m! zs`Rdt7^!|to&&#ROx=88!ehYM&dv@&v2&uKu~9avz9%DjlIchl>@lI&3=d}wu7Lr> z=EuJHm}EWfQld?V)`Q}5Ry|Wzrt+$Zdg9i>nrqom+2i)(H@<&2gyOtu5BTmA8%EQ= zeUcIb<5o)QD_q;}9I;%UVR>4qa!%LM(h`Rtv_P6kkKcRZy{$HNb#8eePukp+pvIK0 z??&m<%i5~knUnCx_c67x$uN-!@bO_Jo}B2(K%Am_Fs~o=j31z`aG)xh(=bW|t04c} zF3d-(pSuSVw64g)e;oZWd2nK2|Bf$@ZGQcFdAk47ZM;2_P3Xr@C$+Q<+VAl!v>))S z9rE$@C9`0b?ZH`*%ljCA!)Pc39 zY}L{_2$(pau>JxUzzUzT*+S{LWO#n0x;Ye)oENL;B)h~ zWL6!FGJL$NYf(}IP#FMlX#gbG*Oop&s5jLSYY`62tGmFyJbND;Zjr3TdZEGL(&w@J zN4{+w%#dL3Le@qSEEspM8IEW0GJ?!-2@Ow-;YNnEP{Pm0cNwHVbekx=222uJ$-lkS zGfT|QqH>2~SkKT<4wXBav*rbMFKLzGM)wPGi=7wuyY>1r^o(EzoBoZBUBen3SC#fm z9ILs|!Q;sT_COOgU@fjxKEO73Enx6OmkTpM1hR?TA( z+7fxDvm}mTuL~AsgwyMT_h(k>Mh;N!+OcDw(7T-kw7JydMzPVJr5HCZgG)d`JELrh zgagL_X)2)wRyvAfqK_ZE!B25hD#qeqX6c} z?Z8oZ4{{0+YuM=Qg)=5*;=t+*p%ab%5?FkE!0;?29YC0gZ}F zs{?!2Rqs8|o}8d4#sTA*NVG#83TukQCQIYeb1lD#g|V5f`{r#$j2}LgszB^Ms=gC! z9P5>DN^!6z*q0tI1H$mo+zR9gR`jx1l;?KAne>D6Oh0T7nq=y;#^GK7+}^(BtZX2E zL||Yhz<=OhXn_yGSd>!vK(mV6#nCa(I30Gv2g;>TWx)I6U#l(Kh0%Z(V$zw2ps1+Mq7hQL)YF?Wqa$A88M%i>I=}sCJJq=F@s#MHg40Y&>eV8u zpZwJ9Y1&m;w`(J{vg49D%f8+7xh=zzu{PH+l5lK=`Q(B|-Mvpz+f{_@Z$y@u#R`SL zz7bV_2s;zY;U*m1m*AA2-jSxYa`kYq|Oyd#IMKe!QN-=s+a7Q0m1@vGq!3+35uj#IoU4!$3q4)(LV7a-!Ts^fYFKXq;ULLv{qbZB!2W$!(XEDn5{V4D$m0vJ9ir#-!oi0Ek zf1>T%x7d{+&7|V5rMWD*#^Tf}2U?1~H`neomAe~OB`VFIeCeQWPPG5?7L&{)v)P~K zBvrOU#j1BoxBFoHsPKCA`3|*pVmmJRhq$Wk7rL#FF)uc4gMiwgS)Y-o*ZJ`os!E_ga#xP0!TGx zdr-fm*xZ3lL;GKL6O;f!2sfGHQ4S0b?@`=-1II>p_H_mo)u06IW_S_6krzh8VChMk zm7vVK4l@@En=%cp!R>(_V-nVh7_w~xUjaigh|-T0@~Z{)&lD*1-^D&Z6m_eN zOweEpfio0R-j|$`@}uaksLC%GE1SZ;$7qS=$%#RR@PrHdLDzYHRgp_8b=)s0Yhw{R z`~KGkL(1zK`C0*{?~`)otdb^T2<_(86PPkiNY-i$g5Q#hJa;Ui}OaCymbPpVN z1oOWcx?rHI%Rl^pDqToW(CfcG0#PGwrgji34H)9*T6}6sa`N{Qm+aN~#ZFsEF)@?O zOCGBfi%F0;I^S75?wv9(b`)0&0w1aX>s0zbQ%1$5E+%>}_Nk6*uy>!x2PJ_G{<=7s zV7vf}ZZ?D*5Q-2vBhKt|$)>Q1AT@`&A59h_Kw(xD2NyU^d3Z1@ZP+fiI`B{{I z=-B&**T9f)Bpe-Ksmab@2UrdoD@~c7Jmf)_pEku4dH$T;Y5J)(SAwSW;{LqS2p6fF zr$122Zhc-_xPi=YW0TwWpA4tk1zGo+i@5e8AXJJ zohF^YTVJ53e1KA%EojPEv0-$uRD)YsEhpjB_43XQHTX;eAHQ|$7B@+|h=t$cimKaL zRX0DTdY%e=9fOfxiyMYfqsupazwBVOS^T}#qvIz32_Z^t^x)ez#iUzwU)$Vd`0vYu>G*<|lR6tYv1kv+2Nmc4hB?3rEm7LpYi zA)BlavLkZdx8M2y&$+I1&UKyV`d;7P_50TIJokM+pYi^@)_XqjE%tiU@z2At?SJd$ zMfVwrU$pQ@%&yBmH75Mbb_z#|mSDKP+=E{g#APv9U44CkT3+$8O<0tVLTftW7RdJX zZc6Q+FQd4a|Lt#sz@LV1zQ-5XcqCe^B?i`8Zrl25ieBZ1D~AZZA>{nDaPpfs79Z{( zzCf(4SLKD8U*3W=cm)Oj!@KWtbCsLyrXQ!KUsRZ)B3P&5e-@lmQvccH^1yLOAHK>k zY`fEqjP=fRdWC{86tXlSx!~a7fMSYBAZbPzVvgZ(i&Z8K$aqxdl~)}@@O4pP-Rz<< z)x*D4-H(p^^{<2?nl-%W2%Hq9ByO3bera-FR@P^1)t@;29<-rClh75&**ixC3|#H!1)U*o&9(e3^(%Dy3~M885c z_j$)CiY+n&^Ix4@5>aMlxO&dOn{Kl>(<{oG`<_O{d(3OENkM%GA~R}YEI9vLOyHBY z_D47QJ(T6;xwwnUS@$Hb$hmiC&^#S*)ynRdho8L0Ydek?;|&%u2;niImL~IVdH^Z# ze~)@;hKY$=;PcdS23B_XRky^~;T(QBf_YEP+()VDkpuG3=DzIbo0kXP9emNPuF4n6cWqW4JmMNoE}F`_ERZGb9XK8xEE^QB7=ncv^$Vn z;n!jkKHFcY9+_>iYJpXTwhN>IM=uaYGOztDJ=_@A7-aFT{=`|C-yUBct=usCtek&x zyKQ&?7=2@}ea{Uag;j=TB>hTlM2=WxOSMPT{)`w63XyQ4q@fK7hqKd~-@g4$D|~U0 zSAphww$S(NVCq6A4IG@_J^<5HLFpk^0OcLEe^5I0QQ>{=q3~uBe-qAE!T)$fG9i@$ zk&{tvp=`r8$@ldCep&dB{$;P&FURMf$7aIy;X{HUq-)^;wZcG|LWZ*M<=}KOnP}{W zI73LQYqD+K$E`Z=Q|jFXo=se-c+5tuib6@`nS5#04Cr$ zFR?jH?^|bm%#QKqJqjkV7ZXuhGcQak>g6yJ72Mga1HB$9Y z+)3KvRyXAWAp%aB4hkjaw)i5Qxdy2}td?&)8Pv~dPSQMuGtF#Z?(S2phx4(O=!OpE zGsE=~wGO*Fy?fAqJ6P-F-AiSstWTY`8BC&)K&oTMTV_}0SEoRV9$b&DmpJ99mUjJ6PJZ)FphB?3_@ z06+co!peO!z1e*2rXy#Hw&E>LqPVld`6ok6T560Ihf&wlKkZ7=R8IAe<{uq=Z{Tvv zDABLP{@}lo`=ivjR<^wM8004s!;=wlzBX3{V5#Hyh%Yr8CUI9O=3cl3JVo95@GcmfX`BS z0^gBr72#J}N8VD@;tSXQ^GC$+M_#xFaY1rH{h)P0!_3Kg_a%n{_!2H!F3GyqRn3UvqBlLVOP^ z9_(-E@IteeQn7?N2$yKRH(4iFN(7U}k^|ffJsN{_=d~0yJZDSU;|dXNc0*X*%TDtx zP?SL@1*~%3JY0+$}r-H{D4iQkTsVy7jp-Md(?Wm2M##g-^RK3E11i4^W$m@ti*;9WW;t zG3-k^e@}w8x`3^?gbN*EWrpAJkK_y9y@RE?BcFgBx6OXXwnJKl%=hyQN1q@o29b&G zjHKPYhxRs5`J@<)Jr4_vedDXl>t5b=9HaYl!pWUHT%MB?Wqg3OT1E z$5sLB{&4^H67E;t^IR9K;DTW%%E;+>+{_0_PWaIDoRRFTANkFS)SG8-=u|z8DYY~v zE;(}Lymj)k@^~i?gr94(M?(ZwbmLm>#c9GiIoRkB(q~>hiV!7A6LzxW0Ja_tdF{~Z z(^G;W^o4m2%}z=)**zR7y{g{iyGpk7l>R27Nj;Eu1zVBvZ-+AekqaC`^ z52Iw$B8OLYPWHt$zBC{CoV6nM`)fkP+BFb*p#-@+I2sg?1)<7%L7FV>PWvLVweQ(L z+WTe5K{$NHNi?Icy`%uEERphc-a%l^1K--%sC72s_l$APb54`klfmxL^ve>k^GAEaid(%8cUv=mj~rRqz0LcH)9n_Z*=p8wm$6W?hb2xxU;ikRIIbq z|IzI?XYH6EM~02xu&@DR*vSC4#%w26cvzN?+EkWuL*LFMwI{-K+BNAFA=yiib?ejg z(LS+d21DSZqJx(p8`xCT7U1?JW2I!_IwNSJV&mfC@`z*kBxYAB_uR7}{KcyR zw_m>&DpVUA-WEPMPT0NDUkO&o07DW)M0#1h6<_FEZnt-y^X>P8CGIVhkrUA85|Y8- z??x%fE$N9eCNF|P1LNJ^(VkHE|DZA1cefJB85)u+z zMA_QmM2=>?f4WQD!XA8`CJbP=8c4iFyA_t}J)ca1&SW+qm=;aqRhEatI7rIV)0u%q z7}&44_IjAV`PYG>;gA8Lk1Y?>kUkZayG+2xQk*THn#n>Bw2(hc)K$XWh4%V|6ZsT` zyn@HAcwos0tW6LVagmBjs4+kZy9ppRTWyb>@GpmIIN_uomlbZjR*3SBC-E#sA84w$ zlI^M&h$iy~%gf7Es2r4kxzu(%W>zi%tk1GM!E^-h1hgFnAW!+Zxo^No&Dc90T5u~4 z;f9f~BAw*y>_y8jt|e>-1aA)O-%rJ2e_o|c-4or zG-;zofOH8jux_u8?I93|C=Oy~w+OW97hq;X-@bW5)G#J(baWKX;w033sQ{q^QYitQ z$Yszbq<}|VlKJURYx5I03Sl~l6|+K_hL-w~r#<(gmrA?PFBj( zj^``{D>qz@c~BI0kLc%_<-@V_-n4&kar)^Er27$4SjYleqJCwF?~Oa69x3t5#{j0= zRL{qO#SFEjfB@BdV5x2)!3u0?WzgU2BA0giOG2h?khQ|4J%xh_{iY0hsgo14|IQ4(m6Z zNIyL8hz30|v>Rs>654|)2w-xh=foHY7`~1dl&kp-uLxYaf0 zUQkl-(}1kUTuqHsbpUYtRq)2lC7~8pvr=gA-G@m7-v2qY!k`S92Q{_rzg}o;hDn1q zLB5gf!GKHw6b=L?kcP zv&u6x)mp4k2{r_Tir6g;vud6n{`8J6xhEvo;o&`-ACiPQ`X9cPHCxnd-K*$1rbnx* z1fT7pA#fXzk>fGpw?U#T8VqS3T9O(v7h&g=^ibi|4B9&p~q{RP_K^qzw_N*fn@IgMEC68C8; zK5@0zKYTgd*Kiat5*LltH%Z}N8-sb~S*)Jlr;idtYv)N^waj99gY!btmwPm70y7XX zBJ8+%c@{v70H^ePITOnZBt$IUQek0Cj4HFtl|g)ZQvj7CZti{uF$hSjZ^9si{*BYZ z5|FzMdTJiSn(efJ%bXk>BEXaZjQ_PZA2b-gcOYa40x@zNfii&|46K7J$_~I5J$ZhM zNT^mAEn8S~IuN2OWCSny7@gx;SM0CdZq0jl|KdfSoN98o;<;KKa#S>V@rrV~MZ&Cbmeqb3;8yP7 z@dxbL7*yC0VQtM%1Kffp5M7{W0lbfxhQA$od>tB~FEpKD5MM6>-*X^Y4dk-6%>0Io zS%BBkAu=oc0;X80%@m9g8r1{P2|u%5(Wa2A-r4;BgPxzd-I$yLo)B(_f|3%AmIb%hyP~3h11bNU7RIg-%jW1a zqwNJ!tV*?;N(vY;9hc`q$bM+K7@3}1wuwYi9xA0)X{ye>K1}J`;^0+~Gib4>33#2~ zJPQ9<;W6vRT&yIzCMv0>_oi1JJ)bNQ_v_b~{gnPMF@f0h);@T)T4t)M8tN#2xV?al z1d~?pv9~Zcrxac0v1jAk3(h)d4iz!`2a;F4$bhn(qJK92lFWUj5UKoPwuPD=9`Hf& zE>5-s`wyt8fCoB0>PmR^svK5aa6^gEk-AmP!2GS9=-E0146P10G}KfF`yX<&VhrVD zx0x&kbxa0x6+xo(9+)0jqT0bm3i(w=#flhsKm|25dY2pAhq~vk^F{$T_V>s^6S;kb z@Vtg|{JWZvVSW(oF-0w?T}wl5q@{#5T(#cG$I98=j(ESD7g?cxXN>J;K)L@LuGhbr zvho46x?)1L&6by*-n_oYR!n23jVJB2GSofqFgQ4PWt|`_5n}Qk*n)K+?pQ0gN=BXn$7VV! zW{Mie1mD~SM*>Pa29VIkQ8T}XtZLv^##VP>LPCI<l;thVE4PA zE)L{gD^o#D1bGudexf$@A&aoXyT3mQurYhQJc;V)`@fz z)cz2oWBoQ~&tUG>yg>y=6~!RIE?~IUJ6v!Q3e6MyODuc(K$`xk%@}pk)rb7u5MM}w z6Nyx$E|%R)V+Y!S9gaZv?it7t8CX~>wMVgh-B|{V80cab3;cA5XfM(&U4TI0-M>dJ zwa?olnLr^Ld4{eG4uGJ-0Ok`c_jlMcokZf##{3waHmYBztw(4t*-n2mtjpFgnq~OG z`4%v|a9*}Hg6-4Gzs_-?)fG}kTD4Z_1L;YCU#GLf1!O)@32~B&ehhhNdWDeQ`qbU3 zU{{U&&2M$UZhiJ{8DRXKgj{d4;QBW)4>hb0Y2DdP^|H#}G`@3E!_vdSra^8LmLRnz ztR4MZ?fmZU?pN4cDA7;nnA{FtI-0t4Gv&iSL%?roZK3~yf?v4~hvBe%XWlw4R@@ zhD6Jq6ccu)Kg10@fDp^CAr1*^HeK{avv%ioxD2QLX0%+MPm7(*+*YYEeX;C8%hFP$ zREzV~z>-t7L2im+#SxU)F3^$Wp1%IuXKEE!K?d~@p-G)RUxR?S26n*N&F zv|eZ{62=ko#m8|X?sZHs;v)t>C6vEpz6S^H5pAb06}bVUa^pr!Iy)S9?Sw^LV#eH} z?Bhp{qDIP-rzcLe@bIo=M()yNAt9oY)<30Ya&sm3#wpnfPj-ev9Cx*W5Ues_x~neG zRvtPdo-~LMzpVZ@VT&1`kN5^PF??O2_Fqp4>Mf>_7|JJ~k9IEYmHeZR^B3^T$%a)5 z;8eVApIF{P5&LE@GUgvfC?>TH8#_y2+bmS+BqSt)Z+YPS2`_+-YYccX>=>qKEQrxY zAQTxEHQ;*6%k>{{sL8lWNwTLhd;)0iokxD^uO>c6`R5A-UTX?jx5UI=@-z7=HJxdk z`p?JDPP;W^XMdit15^U=5y7{B9mA1)-xDCmrDaW$HhVat^3&+XUBqje-9X^5OFh|F z$LDCOJ9E}~?#bJ&Rqy^Y!ri8DI|szFWX-X>t3r7Ffn*AwZmLkhw%G}+MPFb#h*KtP zgt|&#NrUURsSJ=|O8H8K!4vfiWx;rx>H^R(u*63UtS-#t7mWO0F}MCql1uLO5@5!J&yf2=4^r z2$B=kOa(+4aC21SNG3C5_kG3=IxF)t#M*H%Cv^dS(D870pEQ?LwXF}=tP@Esj%waX7k0Tzt4DdA3NU^>9nqHc_X6F=%h02 zhfQpjLw{|4{3RYkK*cIQ!`a=l_Q>z9EQxq?aWE&z{! z`}S)91EG*%Q%TDN8Jg7o06aM?vM5Jmn35L(h5A*gDM&=SnC7d!Ns6X*`chr=1k8RK`ge4(aWsv0|r@6;zoJ`>8m%Jao7;$MN;+1C(O1wEp4G**M+Lj1rLn%^iuNX+DG^13c` zR#+Oka$_k$RH+=j$s&C?`tCwR+x#^0bHg>gkG;*FY&iA`nW*CKaTU)6hdio_N7p|z zxm4cRIz^~6oxDe`FoSskS}%*U{j`6=;2~jv6OwueR06-MkQ#KZ3eL9pQFcqjfcbC( zT)>{fba4YrU$=6fuAZjCDp18z7V_5~TNFR$zis%8JQt87m~4m92=1gD=00LaVH z*bb_eB&*ZaN5qn zZ9WC-S)V+T^Jw<+54FX(I8vP_sC+`?Zo-c) ziST}T`J8NB8VRMzmwZ?z;)VwFSvLFw{aqo4+UriydX1E8H}ZE|6Z8D2ReIVVDx*=k zO8Nl$;`EO4jlU7$N?&@TvmwdZP?SEMrJZscPc|JZ{gnfUQME}DM8?YJd(Uw%ZJz2* z;^zF95nODwI&pHp&zpuB0HPP(G0jSvYRh!h*pPFcle1dWBfs+J02e0{;rerM8V9Qp z+R3rBDIMC$#r-CmWX-B@qWK*@j+CfRu_TU^1EFt2!S6!GvEsW+N1{^|+N1dxV`Chz zGJInM#|r{1Ypx&5M_Al@(JEfQ{&wg)%a=X4c4M?y=6E?HQ2o`Ndv=raU)%KG1I=fw zgqQ%PEp%?8Z;nNpEK5>i$)fZ(s_skSZ~O4RP*n2LAo{Nlzt?P8%CPlXVKH8^lc1sT z&xDeJoEWA-iYTTvGKb zb!O$d)V+t8HNg6njDPQ(!MSt$_A5h;-z7bbH%ixbh&*yA6sd{doWaS293ZHRVMo_m zJnUHT9Q%2s(>3MCM2NO?ups)k0l}TGhs9Osd3tj7+H-5|myseKw-9U^(dtut952$- z{rU&Ze8g?`-i*HopxdtaD`Qo0!56_~%z?&NspEA7N49iz&U)LKkp#U;XIF{FO0rVI z;kPm>9%RvY(&DOQc>B3nNIjZ% z4TJl2%OUy}H#RPfL|yk9XO7_Cpu{{$d?z8l?+uG1G%S&LC%+~rYvE)aoesUv2j{Lu zeTC-{bLQ(-l+q^-kNe?gopb$?;qz-}arAaGrfLl?xq`7i3nQ=LU=RZ; zdfz+Zm*bCjp;Qt%D}^x^>k;jJ3BQGECTmO(^xyG~WW<0gUkq+RX1c-r)P(A(mTuj_ zuT;jn>PvKYH&rl-_E(Ok2$E~gX*zcxGiY{mXlL&DMy-UCE?d%b$m)ho?HZzFTL^&| zh1y(g0twmYWgOxGtq+uAhDX4Acf}P?8aVkN@cnqXM}tBLZOsga-xtwsW14b z!Mc!k2Sa1nABJF+9a=yPDvy~F=B?}i1}rKHev)w~s4FlOyvCaNs}%U} zai+R)-GEH@^u+nRU?ejizex0$xkvcdxy5@DD@e)sMXhHx2b@jhb&65VfScRHr-p)_ zgqqmBJ~`VWdC{jS6$%9cpC|Ps6gaAAPB&K5*f?Ug3dk>A?VJHfwn$z)deQiGQOxnD zmFQN2!uRE=^1$z<-=TkEL}de?jz5q~xD2S4fo;dvVFQ|K}~ z=GLBpaTZNt#7LwR*VNGHDN3QFz{&IOIu1s!RxPMnS4SQr@L2EK65ns zi;bo@0=b~Tyr!MU2@VqAGD@L~&ExC3+>{B~P=IM^y_#BA-)hWY9GROT1%!O_!QkPc zXYwM-53Q<=6}b_z&=%aiPJeay$s`rqKe0de?&vB{`eDzTRC&(>VZqE;pXAr{0NT~w z%e`k*Tv<{=e(+u@7|6N@d`cRz&}>c*{NMTIMMXWc8<~rZ4B`tPDrzdf>{uo4iUhr~ z@AqbUNp^^9aqqn#TpwO$<&eEa*VnJWPd2d2=K-ewY*>0+xl|j>-GUp1l!Wqiz{l)kP@q1Y_#f#1N56bc7?$OA?7q0JNcJ>m^rMhu@#>_B^<^2a)0c_5bu1l{)Ak`vf| ze}cyp@=$tRy8%u@ld+&l`@&u+p!*OR1ooRp)D-}%At38~bNfkkj3$?*Cpk2t7XTV0 z^K#LprUwv3Bz!r7>f%M{Ba+p5#~1c(s_)Wgk5!kiX_2<-aNQ>da^}j!0Ik0XUj5?Q(1*1tWI?_bE+ zdPiLb;)Z^Wnj9erCK_-8BgI%X(-LIQv8pAyu9go^x{O&{4HYOCpzcg)dii&I=>`Pf zw1!Z1(Ypbf1K16%Fk<)M`ST+bDhB=hghLmPF>)mEf6zSf!qe3-FbO$IGn}$5fOWl7 z`?}39F^r4y(q#V*h8rhAcn^feB=}0?kKSE{!w?vfFif=*$(^&Dw?3IS+6VS+O&NPp zzam06^W=WfdJ}51ay?oano+g1(-U(L~{f=p2n^-evrY2dynXYKGaCDsJIr@NkJ25;cWCeG(g)wjVLwZRxHs7-0I-mhNbNuJ zf45N_!aXKSJQs!hCrEBb2^=8ltOehqiHa!C^JL8g7)g+8LWV?f{cgh4dc0NKUs13S zT#49^L?f4V4D9{tJdbvU8AKc&Lz7{UZUbZnJQxIk-ir|c(<)>sF2eX{R1faEt;Hh} z_T(tDJCVz`7c8j2czFyPK0{ktB9KA`0cM)eu?j55H(|W6vPnyPLQ}24%mBX#3=|%{ zGWJ$Sw|3b}p$fCD0!9^g0aX-;n$5dkc*w8tEIq>%lQqlD36*vH&{CKQwKt=tUAG4j z$S~0UUoX#Gt~yxGw@66lGXd2e?CsH55DYt_7b|RsFt+dkcsT~+6GqJe^uUEH(Q6>K zg0LlExE|=Dy}VvZeFQneNHrq{mOilOm;g4{@z@;_F6A{m;U-83T7dC8{v99*3U4%F z4tTOZ&`?$!ZlQFylZ00ADe#t%F;`eQNdv8Rp<~yDC!k9-M2?W1ARZC|+1Z>$frp<0 zAqPtEg-2JuJ=LYovfO3Igy}}l9w~h` zo%BpfQDn=#Bu;1W{*9EfH;!4_`LH$qh=(m3Vy6n;WMCDZqlL|#<+rw1l!IFzS`6;8 zn%oOq%^>=hFqxiUL*6_XmGY_(j$@iv3kTjZVyOMIrc3rHqiJ9EJZ;U*&6UOoG|bC_ zm=>VaVqhW4-@kCH1kU1Se>((f&FMh@*Buy!fLoD5W4B2)@H}n<`-?U<05^ZBP7ra-)<%05Td-4zI?S~La}Bww$Onci@=DRSE5*$5 zosUMQ&%LHl!~{{(XApghx}ZXZ5Bj_zZSN4~IW=X#$DEiNuOrmNm^2vX0=g-0-UN{W zi)T>>SOi4r#H|~wJ^Tx)ieC>b4Gkw6|Lqi``~s>G03z^fc}(Ncs%_XaNcsE)?GG{D zzhPf2tBa1aGSLdNo~Y5Xxg|Etb~C=Yxfw_p@XRZtD)PjojOv> z=<|e*$IuoX>?}EYh0vk#Bk;J{>M2kep_VLQ?mKk4hX)6t)&b^^K54D=*0-xy+X)qt z_qA=xmxl^zyGl?886tj}LBbOZv|!!&4(VbL1qtLIL})?0>1%M7gP{rS z(CX-U8g@b69$iFXkEoV_WqldEVh|&DAE|bjf3xbSs^(pHr28yd#icfBw#w<2?KMs| z1*-Qi*JVeo&dZo|uU}uDQc#llDIOz!8b?YoddS)q!xYd0LTxp*@P0oqSGVxNfN8`k zqfH!BrG1JZa2PUDJ%J~nkO#+!BpF70j2$^5;sCA=K7ma290it z#XK1{mZV?FYj|#0M!JP+TO~rElg3NDbLcjS+}2Q0Z=Nj zRsOySt`BN|2zOhD@1&6buyHw2EeER%6c}{zEF4$@DN|bn1KM{$O|1vwnaN~ex5lKw z!h}FQ5C20a>V#o(0=ENDRZUAyFmoB{NTHd7&cF9hiuPeuJTC*D8>p+hb>^K!VefKH zzRJrRC#%Iz17f8lA=~7R$LSUa1u13wjyvFd1c#jDwe=v+rt##P9%M3M>N58B8!$)a zokSCJV z0}Gbdz-WZM59#XZNfvQ5`*9i?7uWx8CW6Rd z%Q`ct2GBM1ung8Jh&No!<78oJffk1=zv{Po#Nc{vuAn=FAxKDID^rij=6>6~3;wPH z&xTLRe_#b87jv77Qfz?K>rd1ES8l~Y#hm9SIe*KfbOaLLtSl`xC40+x_-c`VQM!LS zH@X@Xsl%B*{OzM%xz6CEzkcEhsB%SZDUyxz&{jtl!xU!MW@p9RN5@{5BdD3r>R?0N8{A3u2l-eC~%Y==S@Wz|1Qi$8)IosM;4Ii)ZcV|ys= zRZW{TvsQ3%yh4y?X&`kt)Ya66BOeC`lX408>p(S9C!}(Q{RFpJM8`JTT!dXAyYSFd z6YvTs=0TbXep>i+3jsn-P_cmU5DH~P{GPy-9AyxC40sn zJm7ohB(D<97ku}bMv9v4sR^9xYi7&=|Jig1-PPWvvvUFg@_^4*Ce7cUVBaOZFsrQ0 z3Cuu??#?j#p~|&tZFw0t2B!xQM?qEeq;bl&ax8%O5}XK#k=7?MfYt+<^v}AWu+Un3 z(O9%AS5K*x^H#`{W4 zA_?d7%k993&4{BcCw-4J0Q7O@3D5-n&B+VYqKjJ)sgQN+G(AKEfPD~Dr;M1!DPv&h zY;M$)%Y}AoZ8*QCh~YcS1QPFu&PAGzt%)@u{aGkV4KhD9ewsqqi1YJrKoJAXmLNDJ z_WnMXU^rC8FH|Z-v_UgjZ=^oJ|8)yJRlDT8Aei3u&e@8@YI+um|KcxLx~OAH?P=_= zETR$**O`m6w0ejqZ3nj=AA({9jL$4OuJMv#d|PHJ>{1AN*Wio{(!!FUURHHc%rT#E zF?CV^nWj9~{I);e_#-+KC*;Q$Pbu%H`%n(yAWN&bav2&ZIwLwCM-J2&L}Vy~cofAT zh@0i-r|Z~*AF8+5i-X7(_Y}sOO75fXOTIfF@;=c-m?nbnf%f~wUXnZS!78dmqGU_u zGYbhF%gf7Ta}_@eL=p&lHvUfUxX*i@>-b0BO31?Tho*Srb=$%{arw<1`&X*DFaj6_ z09H&;0{jI`ex||cbB*KNU87G0QM4{Xj+w4aKBEsJH^LMy@=%st_htkLFcIQc{7p8` z)u*K~QJj@$KCkq&P>GS`lkLD;b;<8)G+>=^-VJz_hn8c2Bx^!)G@uhZ?&oFjr1^+p z0z*E6CmcGIDCEOu=cw6yPWi7Qfr*2Uq2F0X9~s_DGAQxr+V(vqPFpuO$N7vs@anvbfDf^&ILTK_C8-h77FU z4BD2`hy@;%n%E&G;*c_VadxAM?iYE)Ca!_8vo^JVR8H=_Jk|*8{>m}YW;Jxzr3oXz zeJN+|@6szMkG*do-W8^9P|*?RfA02L1bj_)1LuDRqcxZi9jGj0b^t0hQL~|oz7e2T zXhSD~ikv^q+Dnrq=ptnDA6h=0CvFK;P>L2`X?~NV6kZ1lQePQly_8v15Ro(UEpf+R z1ck_$yN>@B4;WOWe%>{vhG&J7S-Fd~w;q3UHEHC1EQS|;Mx&1#*#o^@ZffF%8zt<) z(by}O=ff^UY#w}4>nSKz{>$KR^5imf;jO+)&V&`>>Wn}(-S@A3@IvXqAjLOziQkof z{0wu<^AypG8B#G%e!7mm?FR|v#tvrVw%boj2J7eB|D{mqpp`UA;znYXZKN#C-pbLw zN}Qu%OZx=9ZB-s;eD2^dWxxYc;f2(-O7lj;=*-WAnxyfd77uzGq$pA(ef`!M@@$#t zKqf?5MlL2^PG;px;w!B!stD)i;>v#5n8HyJK0H1f&zh={g`TIEWqHi+-7;?oDza}e zGHc}`SYNms|D5iwsj*xVBfS!J5Lt6(m9Ky*l!Xzdbt5|_W~-aQ-tl*hXmD__AV=)g z3_Go@IcV1Xr1{KN=VH8*5syjUV+K;BOmXun0qbWn>6CdbUm*LOIqOqMew|&mx$Wi* zzC456)$^xQ@v(_T+^#|4agT+KbU?Ul27*#T0qY;CnB{}Np)C&ilw?nCa|6b@ihdQP ze6IIsLEBT3^^%tpAZQp(H$Id2qg$YR=&@uFw$G@RUtR!K=R6z?k*-bF2m@HEov0`%rQ z)WIyi%8P#qh`^S42}v<5k0is*rdn@K9(XC#CckOH@ghZNKDuCX#ugH+j~8I7E1$IjrUOdQE}ibxUItACG9g|IBHj z-dz)|>X0bxytX)glChLOk#y7p{f6g0&PebSAGY!b;l=gaM*sEE(9tm)e!K6}mwz7O zUN^uHR_%IE0&T3sNDQh4zKw@#0`RD<&kzXt;jMn&i0@TbPi>k?4}BZUi_dO7^>j|s zpTM^E{j>dujN-p6e;<51mi29FG(3f~deIYkxtr0J!Tt<8Dsal4H0u^}N@ey)DXVe% z*^)=G;%I^r_0YWO7xgy`UXIGdnZ6C>DuN=ALr>chhJ?}QNM8Xt%4QJ<= z>|$eB_>r;IFi1u@K+aUg6?=Z7?3BQv*S}J^B~7pNDuT=kqd5;p#dIrUS;?C(RkSND zRp9|L^Ca*QdI^5|MvEBkFpK^sE7_IRm3>u2z>XdDV()5gtY0}|zZbka8Nz;{lYYeA z^kg$1qcsoUeH(U`HT`CcMy3LMflN9{V8izcp^;ZtQ`0TXhh6WLe|TjH(}&{aHo<7l ziZ)}XcSA~GW>tTdS8NR4Cy%QF^-RQumAKB;RI3SIoI+)Ynh9JFIsBl1Cf6G zx0b>fI*h#~mL0AOCG+3CEv+Pz#9IDY;uJW(V1VrwY&m^)|e{QX7$QSNDRxF(=BDRqyNurdb&&OfV?a9tWz`^}E-q27@Cf+5)?RJu?$ zBL2k)1J3Gg@M-g(-Q!Tej7~}^rgkeH9lzup%7`iEFg&Y7SX8l7ql)B@wfy|N^X`wW z`7j>VPFV!Tg~4?t+f>>2=M?HGQxVDFM6p-A_*mmA0*jL9@~yfLh+f0 zWl2-&x_$U#Lu7C0wIBn@(J9?x#VKN$R$I0sJ1~eqb9t$@hrj%aJZR|=LahRw8j47| zx^u0v%C;UcxlTW`k#w$qp}P|iyORNOuX9f0^s>TgMz59=^lpWFyM=g8EYFF9k&w9@ z!<4C}jGkVIcRcAx{!qL^0voFLhckR`@d2{>YaFq(vT6&X9d;#Ajy5Z;hLCf#``8-~ zurI?=p&mkOzL=HvnbYx`_ku_iVEj!;HDdNn@B3N$RYL#%{da?w9y`nB4_nVFN6!h_ zjFilO$!LVu8QqP(WDde~wsjkyc`C=RKj}#%DqKvjhb#fG5{=yS62$MP{TW!Ki-dRRY#n{DmwYI1NKKhM<;;r|^OUZSr;3c%-Pr13fGh}Tx&Z~CTV{2&h?tlM zz%Mdszaa+`;@&+aroMlFZ9<)P#}&?faOMLpWF4B?n2US^VLAX2m2fTv;3hjedtq@A z93K*bfFX+BpCYwjJ4AOM6&QiismHz@WMA^Hfc zQ-#e04YYHi3}G1i<>zLk3buGa*NygP9WK3e5+-I!(lnHC{FYe#vrFmIQ$;3+9mdxq z#m!0yy5F52i8%H}j1+fbk3R8{Q{tjDF?2-(B;e_)p%H|c=v4^iLP*0@jq|O8DmYj6 zBfG2@85qt_^XmtfMD3zJH0SvJVoJsFz1&9oXrCB{ZwaR==)gOM=oT2Be?mex8dm3)>tL$=W z%VVy&lS6!Ve#UgSJ;_8U?Nj>8-xX)WcoQ^49^2SlzHnh{P7v}kuU>_ecxwpg=^Gx7 zH>`?GNZ_Ol0r{?*xDnSgstXq~3JS!5Az_>9{PJaP!xi`Cbk?M0u7Df0|2{xI5bS)3 zj4Tz}bD_~GMs6fx&W4hkfnPX8g3hv{_KpFwwa}l zy62u#$>FDeh=qH-ygc@cHRr0lUxbOrwLi}u|B`ymmUnlAG}BeDryg?I&e4l*e;|QY zARr*GbzTvChOCW4nho2PE7i5Nx29lG!A&hHn(TZYB$p06^ZHn&x@hapYml&a@T!;} z>GPDcsv8&>0J#U5ZogguJB9;h5(*p^78ab6(GaZvvsT(39%7rtQsa0k@LU(o^3C2Rn>V(QI#T`&Z=anUC`o%Wd|` z%0W*RuS4=nR6~AR+6EAGkQw$F+P7_r$x=|13-#l_yVSBGta5c$aKcA=!`YW5hi8L+ z+JhwAm}TacZ*_*~n^mb`OUSu}Q5;eKE;ExKj6#q!whSvXkTo_o|8f|{PF^sG73AgX z?dAZmLr54T#M#-E4z@q&IRY*XKEdDnhisC65-TqcB#198EZ{`P9PeC})73?}rmJ+u z3hv5sO6>zWEML33Cs6#IUQ9+-7P=1&9+(6*r|l}#5Zru1JQ_Zi5?LLx%04F$Xvs+F zQm{+ll@U65Ylh)W_N6BTj1~f_&GjW0xe)EL#H8p07_ndta)5v)U=EbA5r#Iv{Jyb0F$(zr4-cGIFEPk9hj=b$IjXbyWSH81iCmQ11W`g7_i$xvL?u_PlBV$Y*h=}AOV1Fk&cuN!VeEr9335vj3%Kw$H5v> zyMCQGCMl^fBO@a_o3-}i`?R#C?rxHgz)!F+GyBRgweaazJk`+DM7zv^qyXOgHX#A} z3hk~cmNGUBZPZZpx5DF7#Th9mm zZjVVA`S|#*UcD+QDaptvg}4WqL*h$u@l`L_iEm+};WEJrhYMWHTRG;D3Ts-g_wV1s z+irpx@q_a-SST~n(_uGJ45i+HG%%$Y==BGS@ygPYUqFD6kdUOHxA;HO5$WN?&O06~ zhCy;)47MMI>t5fHhpoCelh2!w;I47IOZZ}5Rac_3DYBb?y)ynl2vGLLwXa@wTa7@qpdV16je!!T6U&Ykgx_fqGduL~2eEh}n(H`u~ z{w703hJWY&z#_mywetr)3f8WJZ;4;stzbIxoC%FM9$qK^+QH)dvzKYfk^^>;u6X2X zy#L1LW;Dqk;FL%%i{IaM0#qJWN04`1z~I5M7(-eDQyKQ1clr6XH8n<6b}vn+N9)F46F2v-j`Qdwy+kT|H#hT^1!voc^c|trb~`(*5j9yO&D|FU$uE_|eiaOj zxrgHEde~br#eSRKmi+z&iJbioc5U33A3uJS3V%eNPPVzVMN3PYH5}+XqJdefBy1DK6-4R;*IY@#NK=k$!wa2;7EOzsTa$!EdPmR67FeNDl97{^e z%&Ao}`2X|!s~=h62XpX>0EKk->s7MCY%}k)+H2S81TF~=p4n>%$V8%6Z~I2}c#YC7 zMRQNeHLlxjU6@NTgD<)imNi}fVDK+#+jrVWDzCiJh$q{Cn3yCUFv8K32Ime7*IY;f8K z3UU0#A3rEjo_QF2;bvqRP4~{zHfCQZ^KX-wQ#jkY{%h;ZE@wH{KhXP$`#2e? zWI2`#AsiR2w~c~7@;jvj)lM;&QLWi#lPY4|C@pP$#edY`yY~c=$~_{4>l+BP!f~po zu8ecV6)u*4Kv1J9bx2NEDg7gYOzQ)E2wDFO%(uDoMV#WKr|4?%%6+1fho?B83eps6 z{YXNc5|->3b$B6MmvQaxx5ZNQ4KLvhNG5(VlXyhB_+=r9hw!U=-^bu|COlm|($7 z^j&-R=Hmu!=+~yy!gt+G@(hW;!eWj4?)A7_mr;5PBfGtnBe!MclcZH3ztH<6f2EIU z$6W_PSDc-Ya}7E(4CiKNs{URKPnhi_7X6{Fw3z+H1xH3yFc}AFk+q*B(J}kDic@|@ z(Ei>>exEebJ%0D2nFCsa0$DYT<}k|_dQY+Ys@~nV%}3tTt3_cLz_K5$gw@hIZqzH; zzPR)V6i2|qE6=O`l~F!TeU@qINU+sfK(~^UH=&{zObwr&SC8U440Y1JSl2-vgdaHD*VuqA#Dq z_TRhH4h=Bbt!&3YH?vo89#K|SK6hChx-z3LINx=3T?lE#5vB`=LcdJy5-qKmsOVWd zTo8o9$q%j({tfSfS5D3uDbZY3dTqFN`T3L(Ga7`3W_jgU_k%n)4 zBZTCu-@c6^J!)#a+}wDn+1W!Gisv9qLs78}1QseD(eM|cJ4JN=iqYS6*sHU5j~nGN zc;4DC-oyRDF$K-~7t`0{g6n$p@F9p=;Cu>C1|A$7AANjeL1+LDWe{2#>zZQa{E-$u zrzjDo!cWu79Px1H&su!N-@S#!O7*`ssA(+;gI9rxKJB9s34eRb zWGWe9oczOad-7DDIqH8D_TF(l_I=p+SEWK4q(MVTk|d%%s3?hcQfY}av`hO8O`2A; zXedQXB&98dc4Ru=I16J9W=YyhrU5lLZYXuIWqxQ*lXto7XI4%!fVP-CREiGSS(BEGyYpcWNdj=m%%g>)bqtiZZZ2TxY8#&Sg&`qM2M1fHB==eKrpBv;` zb!>w^-pz2D)HBH`)gEynOtzSPFn3 zi-X3q?5jY)Ng9Tb>*?vm_U*HjHTStcCx|^TI`(9IkgIZXCzEONN`ks(&mmXpWrL8; zFj4>hWH|t%+YWUfkdwO{qPn#{FnG0yV1+Ni6)tD|bI;;e4f+BxE11B)+ur!ht`kK) z+vd%2ckVn)O&!EI+SJt4moJAE3*oeKR6`>?JY3Fi-c3bC1wa9A6r8~&EvjhHZxyyZ zUzfd_=cJQv;%0Bs;)KpY5ZTgJu{Z=wfX2N_E4P9H5SCo1j)rjS=38B-$S*tc5>t<{@d)_Fu zQ*~P5+Vi&q{`4|_(Zwv*r=4EW#AbY|#e$BYQQ~oS<&OU(+v!<8*KQ0%v?xWn#%Ch|z_WtPL zR>OSD8pv@*bM9Zrdil~9B6BDc^$iS`NxK68bi$Kff$c=U)o9&LzTE7U6%-+R9g}DG z^=iw=PVg|F%j3R`QPBey5^SZha;3$qqC;1$x!(C%Px0pI*s?;qPEljyUbr{=LjZ~e zlGk^7mHzX>^xWKXNVV{WT!c~&x<`npE_!-O^(NPiW7n%2A2e^4xHHc$%=~_*eaKil z@zlP>@T%aSKJ|pUL)x#_`A3R1FrWLp*xYj`s^8zW4=t`D`gr`l!`IlJy=-H(vGex! z1_*TY`0h$1F0Wqq^=p!LNf`hAxg()+PB&4T`O=xp9{QkM*yd*1BTqSa=Zy#B zcQt3$qNXBip^$Z=OzK$yG~p`Z*6#@0Tvz822?VI0IC=89pYC(kdT(x z*w~1SQTrYIxuLi-E-7h@O=q+^jsz;k33r`nJ8ENeB{e*5f1zu%>e?zk8?RC-x-9R3 zUzgv|8)^o9ppweIddqjT@0s_|f?qMUa)*u*3@q{*PTrd&#+u6juDrG8|9 zUO0Vi^9QHpvDmio+S%+z|7!+IY*}Z`Ydk(j3W%LriKkg%^nmX9RWJ55fW?Ck6FMS41*a@_hvlOv{i=9M4~5MD8~ z&uARm^AdVea&1CjGyDX4yrzZ@5NIh>`rW*%7xu5Mb{^bTnCfk|AhwnBU(YR_K+k`+>1`D3WAlOUT7z zLd0bn$*tZ#K27nGQB5P*1ey}0e<0{qUA?KuWbg~dN8Z>yjD3)n_VnVEX^h$|aM6NW zmtIn#_I?`Sa0pGn#d77Z-uM>L;-!i&EtPGIlS6NAzz*gkV1=k!pP7!_-2`G0U{;O0 zgo@7axkDoMx8&qc5V})HDI$z)jE#L@8vJCUt+iE7ULI38$o3^IClJ>$HVz2i-PyUN zqr=19y%h1{_C7IcZcw@2yt!h=^bp$yP~9gPKxmO;Eyt4!%|Ddsv;bD;akOZ&*$Gxw z-#=esv3=kW=$LqJ`VB#y4a^xR0v`UmcUOwT=ijs-owpo$5gRw&0ErL~K%u6-f>Pz( zc!@Q<+u>6KwdXE*`O~Inu1nN1EA^as>kV`}?ZO#Z)e#pDF;_p)UOq$oRREW7J+89T zBWWETOC=MHokZu!!-rQ_U<$w)kqD@(kwxb8q7TCnq{I-@FSrM^%wR)4kR~`dz{k0{ zQQ_hH#l*0pn{x>6-w)?*Jd(;ntYp11zwP=vSa1wuCW0vd_FP!$K0?b~JGTreytk)k z+Jgre6QNnX+WAEv#!D_47HNWoh0WO+j9sn6y7N#P&f!HMn$Nq|O;*7~x%gy_M|Ykb@{n zByLY{Du{Y#HDN8Qy5st^+3=n1xEgIUz41^7!T+xu5u84gdOb~0O+L_k(5a36RA+ww zfdiW=fXs6S$K>ev48C4JW=ulUh0kG>{tl@u61W`HfTMT$kUCp6#|>H8*-N;oh@@6C zV-JIY@)5istr<{HQUDL)gup>jU^S*S-vR#`1`@8d;MhUz03@f_JwQ%`JS~;C-ai)8 zfRq?7|4yUd@b{uyzq$R$m3Qj^?Z~*Ot8E$owfh_U=eG<)iB{N}EQX_cxb(K?Cti^| zZDC>I?*1$_^$bqM`|F)=j7b~50?-##JqqC1-Nv_LW1pK>Qf=J0kwW$# zC`0kDTBA@(ysD{r^O^vh)z;P)D{`tztR9j<`wJHygdCdcd!cJ$@@-(Cg0zoBJ->-E zCS262_QY5n<)fN7Qfcr(D!h?|CngXcFeQn{L8nE*>n!nmc-6sj|D`nUSRoqn?UWxm zFuKemS{ioQm9SSGV`ZSdWF4?5Za=bB^Q5uyx`Vp91@af)ekpquRe%t9h|i&h?e6M& zY?!aaA47p}4(GilQcQgOcmwQjfLtyuE*kl(Z)FA#H4LZ|f^B{LEzE08_Lmy#=v-D6 zx82veS}JMNPHE{=GCemO<*lC&P#-qhRo+J|WS za&5mepAyhxOn27YoN^zz>=Rp?h9)PIZr=t$D1$eUv{fQsJc^Ic0+wWy$+cjufFT11 z1$R~8wt%~OXzmmah2$%%C_&9<^%v4paF4S&C2B9ZZEMpp^lrpjO$LW<_WQoqG9oWc;cLF#AWfHbOC*-&5E9;ZpGapwy-^gghRyXYexka0JiH z&C%u|=LP>IAkxNdpeXR%Q#gXx(96tTwD2hnv{LC3~GzqvAki>)Ra)yf; zRwl3$n7m*|M|GPcN-FACHL-ihRPkmclGu4a^AB- z0We|%p}d@#iqHvM50-;lK3q?J{P-L86BsTzUaLGBe?AwUKzty4!yn$3o@(Tpcumin z$9=f+u%@|axVbFNI+^7I84T56J0bqX+f?A^A?fjrFzjY}1|utP+?^*G@O6xwbEL;)~weL&p(WRl6b`8OVYtvPnvY)N3y-zJ^JeE(4j;p8F$3^&5vcE?kYnvRCf06 z-cv@~1q9NF;$n>dbhl<;V};_EEH>~;n|-}bAGDE&XL5YJ1X~ekw!FkItN$U>7wql3 z7^}(}7x!F`W>h~+*Kb{^edyKs{jH^|&2IhSc*r?e$%BG0!?@twaTJjVrdc0UbbarE z1McKId9LAlX-aZ(ONQn>&gE~1n zdKx2x4R`ilnK*x86kAv7k%)bJ_x?c**4?ON%7}NxFr|f9zpbiSE3ht~c~`}dimKHQ zQQHD7)=IqRZr}&NU8Bqal7ZeoS6mM{oFm=H>B#O3CrK5=VB#%6*cybTJ9cD%B|xHU zbNkb_q+X|~etAtzO&lpMu7_zt)Jp}KsR(8R{eI=&PNOAN;~Yg3&Wclwf<)gIu1(=m zO&*bRsX`;AZ;)I3`_CV2yo=b&(COOO@=@|o?O-{nfB&AG&%^>^Uu&~lW{TvR=`C(K zn4x%W-4*J;@%oX%Hlh0p@3c9BTX*4wZ?dwn;oP`U+-+3L%q=mbkHC#Exc~Kt5t(-a*y?_sulw{g#b50$otXy+>4GL`1GmL zL+HB^z}eQlqb26Qciw!(?@hI-DE7==o?^e~r*RWpLp4|@Jf`~n#yfUnUtoa_074zC z4HF*6f<+(Z=g;5`ck%E@l5x)p38}@yf2ra z0hAWHvi!MkcVf+$Ij&_D6P2bXPx>P*@nLDV9h|!VCEt z?s#ef*YP|jClyz<2I>wC(fRqjEH7@ z?Zol2DMes&zV23*{KO;LPGU|Q@udrng*T#O&)C_OV8$5x4K{E*s;g;fd%C(rP8aVN z6^#^RHv%9=sH&*cC&~i*bgK$n1N;dCC%c7&49orfGBO>~wgVhUQx*<8oTjMZJK#Yv z*@Q!~3iJo9uH>#=sQm0vkOHE?3)kA*Y%Xi0s%L8I z4_pE?bP{~jVB7|s6&jg)`_2+q@80F?ZRw$DD1AfTiq!dxiiqG|e_^NWR;}9`%5DAuu*+`s^or>7?rB3@oEii*m$519I2zdnLbIT&9D46qAmaC39?M8L!+R6EYj&L?wB!XhKfyndt`icEGF zWW>gH;!Qa4eaaOzFr0B9PsRbD2M+RJ{f?UAuEuOaCy;(aowZWGI4|j1>*ObtYt;s zJ^XtERajWfl!iF&L|if!+f>AU>|^kjAA3Jjg_m|m@)Pa-Nm=kx16qO}8LtoiK#<(N ztnobc2`o`ay!pxk_!a92eqMrplD@~n{W{15;KMjuJUIBn;^HEjFnoY{f$N8n#=(E$ z1Pnc+J-{bc;eLFyLhwJ}k5#4RFz8V)=HQ8Rs$Z6S9aCr5z7{yBe{Vv_<;Vn)eG9`H zfXN|q5m4nQ^+C$+zz2m#U z;fsoj;u_-7L2M|F2%jAC!-n~lh~DmN29CJpgk!iJu2$elc@m`qL!D}nX~mf=Gg z9`Z~dd3aQ#*{|-J(z&!PP)G)ClaQ4D>2Ufh4rV4Ckx%SE?No%9esbqxVjBFeC+q}L z`3QMT8IRXAZNxy?w6@SybJ^o`nC#0Ptdozlo4OJMVvt$Mlm(TC%<8l2J-I{f@)9Xj zT24t5`?>FgG08gKnqvn*S-}uuIeT`${^vzz zy^O!dZ*-0PeR@+5IG${Lqd53gM9dQ3tJhP=;m7%PSW1zhM%zAvFEH8w6p7l%dp|)r zMzArW$5mXZaT3Bwt6nx$dP?yxM*9U!><=WEzgv>u+g`lda_I0)6Qwjo6@6UWhc^!Z z0hb>EVeYUyQ00}!RvaM@?t+^~u(Fu&+5=#CT z_{lhVUw?Z{m(pI7WRYfsR8Lz^NsUegH$+sqfj%|GT#Wuxf8DyXxV7nN!H0UqtmyVF zSJcF~vEE{CZ6UX?UD_RiBYV5*56;r0U9gZnUfZ!GA6^!muAboWYAM1^bGMROZ8FLK zsx5pLA}W7MPER-wp3%#&tdMaUeO_eUJh84^DsAzy@wFItin2$5W0Qwq>#)FvqW}u9 ziQ^|$Az%%&KcVUg4kirxTiV<8;HIL`(blG`um8TK<gTYDlQQ0%iQ_U0a45Dw|WF;;gG&eV|Frd^_iD?`;@*27F2xrGe z&D$3c!gal}eJ0?GFI*fE< z2%XQLIaBSyJV(A{c8}B!4slG+esbRsXMpEpI%|XT1&Ox~KxpUv=y=v`y4utG1I|s@ zHh^gZNP`R(jTSV)LOXYoNqg2!Ng#`_-`auG*|h1_+|l>V&E$S_%1?tpmvHPDhAyda z8Jc0djC~e_GjKRSYja^~q5pP+w0Dmh4(U-gs}Hzy(O$tSHnX=*I`DT5vwrF?_PiCQ zqO6G*KDLgUkOiO|0iy|!^lC%|b_TQ(Hx2m5?x8!G2nZ2DWr^PR287tr(c}m+0bw01 zF>qk`N_Z2ao{^Q3%1g{Yll+%bWTpxY3QfEWQ@H+B<2&s$v}ryY39JOBy>HHY6>!gj zr`f?L9mNvjO(7n{(0*WWFdHi?2PY@; zbOS{;fJ2;s0!UV5L&PuTzgTo|D?FSC&E7Ua_t8%4edP;2p_e$5 z)m4X{&rCciP&MXH@2YAlbg*2Y{3#j)wEM+HMHmgo3n2@^AOwENUj+$xCqG96 z1j-VCb4XASP;fgqPT^xe!aqUoT|+kQ8d%4NSA|Y1x7wor*nC5Kx@1+ZmQW_2F!PRu zt$tfxx!c$3Fkt1HDqryj(g`FPcL@OXPxP7Z9F85fdiv}csg1vR^CszbAnQ@z(J=?t zq3gtlch}q;+=gy4e>ce-#{*Z~bb0(-apWg`y$t>f>-H^ER9AnnuJUKuzuV0EWuxEF zekrNn@ZpHc%K8dy7RYk@YR!7$0Q99$KP*GX+E8DQ+lFcW8jlo-L#ijvjw&)J)wy>2 zt^Nld?TT#)b@ucd~Q2uqQd2C7NjN986&%sVd@G6#NX4+`6`>6vBqz9@umZ#CmO5*-mci&=vbkMJ3+i%HzT~ zsK=YI^I7JyTy`h!DaZqJ0O5v9qT}x42Ty}y@1M2^#RX=M+yBmkt5pyUijt8k zq0ifI-7@HBk`yVsvU;0^Y&3t{)60XM^z{Kv+ef88b?|esZLR0d94D)kA`f@0GI-y} zBv}bJa3u^c0w{EP4JAGVHpwRFj(pH_ESrv+t4h?I`ph-85sHyw3_%u@S;+(6UahIX(@ZO|%A-sIBhzSqzm1Lk0sp=Mv59?>6!fkIGARWO*2oZ%QUK0?BSCvB;zYGl#(|>%i z`@MRllB{K@r}xyli3k4|Qzf&L9~MYc4c_F!LJut47dGo?YVMJel7e>T`A2`dHtraY zBjh#Cy=rr$YOO%_C0vi97=tTz8#Tn==J2gBd~dqH$X1l~EKZcGEoO6vfAhuFCxa$@ zbFnFA0xQ8on27#=ZSN`5Yk%i$&VuZG$4%~^vN1+r4@ar-l)gsfl(^*A0B3` zce-gV#js>hH6_?DRB``8!&`6L?p^}wyO3F@7P?;sDZe)eMA*~$NfV1RArTP`h$-)J z+XMU$rcaXM;y&21Q6h-!-i>!%@F zTSr$C!xCR}Ju|;t$)$CsO75=lwL6cgHI57Ra+=-G%zT5|1B{vdIQO6V`7Gk$Lkj$Q z_T<3zN?UvHxq1#z41l<>5wv%7_~T}PB;Zf$g_9S~{|fdpV4?uRus$(Pum$wm7Ut&DLpd1upsDEYJ|*+*p>`@%>mX7;0=fW{qV}Bhl_6GP ze5r13xxHb?5_=6U>5ajF$A1aLZ=)+Od+Erf%{1P@?6+x1KLwrF(7mzrZxe?~*uCpt z=gD*2yHcT1tw@<`#jcrIBw6naj*QSrKKfI{Yf)4 znRh@wQ7kYHV6bb89O0PJ6m+Y)>b$G>g>h#;9TF!`#xc6}vJ_vJmkt_j6lz z-?#TPw6vz>{-tGQrcisc?Iea?qy$DYs_L_w*2WaCnmMM$6_i|ZKdZ?jT~zrUt&zm2 z!bXY!zh3{LqYQ7+2r^G*({MZ-J=n`8Q<^+Qm8j6{N~PIsU~%2xQ{zc7j=&^Wi$4-K z58Ue$fct&*r>^eqXZpcxX?AsQ99;Iu&m4nva$aw~SWSSOvvIJsC4@^G+Ho6Rf{+}O z0Dbiz8yRt2L&KrpcjiF{Lp8(~4z$ed>~sTIAh$?5fJDuUi|f%JWbXmGM~)&Q%#x z$y1k!IJdq(YuWvry69`Oot#-6>XqN_fey=kt$<7M?sKl&C^tX8dfzQ2Qt`7}$Y)U{ zA08_h&6|7CKKr19f6baTE$`o-er9_M?<;aW1U9S*0ADflXAyiLFin$ke|Z_1o~EX$ zAY}*@DA5w_FJHMANxVq(TO>K>o9~kJIRG4pa(*(q?fwf%rBD-2hRYw< zV^{(XuHhky-i-(E1ZDX=R987JlXX4XXYt;<<5KF)PcPqIzhC6O z&Fb~*&Kn+VL$9Ev<2W(NvG>k7%1YnW)E7K=OJd5XjSBM;BX|nZT>Y`2@e?r*)6CfzZRv#YF~1IZ!+x7ljNCZ!k7_Mv)pp{_}Mkh~VH1`ig zj98yf^QAvQ9v-|bQIz=lzEq;|XGEJw_6e;WQh5uk!OASMt06nC6TS1NfIIAI#PE(r zpFy>z&({yuvaoG5Kd{EvhvI9#G`}9cW9t|gW@lz>rV_Gm$HmDY5)Mc~_bx29e)Gia zT`A|3l>NytEmKid1z{HkgET6wZw4E$Mm9p6jTKz2 zWU$v__U!C$O6R#xt?}nYyk=BGm71Rh=U*Iuw1pIKO;0=N>Jn|q${{?`E@=}f?%WL3 zBs=f__XEc5>;CrEj1%pUF++N=Wdc#=Zz|>vrZiik8wCWz1YC3&VD%=<&@%LziH!09wsGimpdKu6b zDRk8eN7DeIE2t4UIXQ4$>>M1PBaPgJ8+V4|`Y>?IxS^rOyH)Bs5*ZuI-W#UYOWx@* zO8x5!Vnt$hew#1NXN91&n2Cdd+|W&t;$>pl*VHtrpx_6pU>zNuF2?uCHLqSV@GA7x z*49>33_#!k)k8|^gVU>UYHGFj&(SoU;dsf7IjfFM=!Y0p3mc3a4`M>^q-y>K*ks?@EKWY>HNfNPd5U(jgCyKwg0^%z&gNH*?HmG zirLaFH+i&Xpt}}83qvmgv<%UU5{~^QrlyS^qPL;8p2oj<{`4sZe5vj=K_hqbL^=8U zb{TJYuCofc|FO^b?h{$iTFpk zsMX$fWtJR1bZW~LBIJ!wVOw7AuIG|>agV{3qeR7R0*(fz5NxrJj}H^)zO;=Rb--ay zm-$M|$t~gwuyUcofQA4eTKE_lou0qQ@gLU3ZR)qm!9OshmN1vy(x`aoaLB|#`;@s3 z9SjZ41}>p!g&Y#HQOuH*p#8Kf{f%CFow{@|Y8f+3(HuU@@+{n`{I3QP?e!2jd2f{?%H z;_?+5OX%mI8Ii5)(?`LLjX_C?Y%I_%`u&e&TXnU`a(h8}v=ULh%#XOsHV+*|tsD`-{lErYQ`lJT*~Y*dA4Dca0;F z7Q#S00?dbXH(cBDL+eIx|FITp&Z5)fbUe?VpWMoJ4dNjZ$!={;TiPivzXW(SB4Xw7 zx@;8R_}1K90yD^|nD+orJn{nrRGxB4IEuKq%@ontN2x!Wv=^>kT|0IKO(qTz{|5La z>Qwy12YO8#M-Y$K9nzDNEiPR6fZK*S)h-1~499+Q>RF0UuJ{iP6xwh#^W9hxr zLyZykv*h56&u(TV*4x3-a*UOOV(LWhKpOR=Bh|KZvK#pVpGuP#bG7vh?Q6kG4+)3e zRh-tW!MKmm)Usin3H_|wa%AN_M?8HQwP>VLyA-{6ccTjGJxrtWr4=+62Kmq*F*$gF z?Su@*97Aop$bb1=6-#*Qi#y?gA9qXT9vU{2vWcQASye275y(tZ{}JdWW3s8s?`j2u zVOh|l0w)KttNed|tqW#qm|Ks6Fnq#O)|s4g#mNyy2f|_G2q`}uS!gE4EmOkjt3$=w zzkGhW&9eW<1qpHrcj(#QU%N^$b%2-dIkiw!b!I;XAHi%{V~q49xr(N7Pe z@(FF}&}Av7sTGO&->m6h){TBFt3AA#*mU_x^OAfY26(eGtMPV_B1^SS!3n2JoOwx? zeLj9tVM<#h*5*e~7=jAZAWw%zOT03`oxVQsy;a|z4YeXpK0Cj^{LW%HW;@1mBt*!AnqeSyT|x0+hY&dbbE+*wOXL?}hhe6YA@xGZ%m{pGP^J!7|br)8#J z=A7*|lvxnwd_S4PKyi>F6L+o*PiTIpJAa6C_ix^yru$hZN6Ve{iW6iaH#kpeK=vE>laN(%G;+onbM6$A8sci1-L^#_|Z^RjQ zjJc^;6?DVcu~Q*N-V6B{Cm2)YS>#w z_4j*kvT4+D6+N(cBg-^B`l!IlftruUmRTlqrsl21INDZ$2;Q+XVx1reA{pWEYcb2Wn(ETun0Um2@W}Zw3lea@cFN{#U}mmJK?P*trM8tBr;Wf7*0qor(@?Ft*Th6m zKmF_1uOB~9R1f|51GPeLud%>}{3BSZApOwY3lYl+*T)8Snwn8<8aLjw_x8R^$Let(O z%6oit6jX{Wl&%gA_`MSo6Y3A1pbpQ}>nUJ~s=cB9V2=2{v`pR5(9p^%t^SgUsHU14`ds#FHIu!!k^^VYg&?BKso{?3_3z@g z%YjDU!$uCrxbAe=StBC2d}}4n`@AYNM?tXq>Clecg*VCnv=r<5B+TadA*J7_E z8ViQ^BD?=7HAfHfovW^EKm1m%I4{;{-5bmM(P6Bmp(*Uzbpk1emTV&{W=TF0y(Fxi z)1kXfN%@Q^Jb2fJ0?kA{d;P|Z+Hg)xWDp7H#dvw6)=htCG#5)yB+5_}pj%2$4?ga8 z@#02oe_)Hg{XXVUJSKSQ;J~DJdC0#jKfu_b@Ks9vt(?wchzxbPUWcvF7&KRa9d@DU8L-P(H#)rNEawuu(ZUqn8 za9{9y_y?c_0K|!e+U?(rkw%EMeC_~uq+hef`Ap1z=RcWlxAiQ~PQ}#1Y892rF;?kV z{t+D&Hm{k)+uTy6`lTAr99v(NM=vfPn)|dQz;^8cPOPZtH|`z^HPk=b`R{5-j1e0= z`Y+TyU*~|_z^;o>&YlL93rr(&aKpxpB;{z+nnp5Fcf=}fib^bm){I4v88RGu!@1kP zZ-2(BWyi;=L4m>mRgLn}1KjdDdz8}OVmX>YX2GL|S zz$#nH`LmdSbeg0rxwx|&)jgga)UI=I!tBOj)ypc^8rG#&d>;iZW=*)0qVm!dk#6)gxZoi1aYyTbtu_xX2?Ox$V>oS+b+>^G(x3Dn}Zjz4Y zcX%*;S3q6xG__yc>=pf&zhA{Rzf!!hcP?GgW7yf(Rl7<$>`=!=pYUFEmJA{FG|M>T_#V&dZ7`deZ|M`~!*q8?W*xO#Fjsk zS(yp$?fQhm&sG0cl8PG)PMuY;x<~!0-pk1Gu8nTlJ*CrLg<*l0kcI;>Shr@)DU5Ui zwwKjg;-@SjJ_#t4-)xI2UeK*6W-IfZBhHl%8CetM{PD&0Sj`Ig5?+ zDC{wvOFtrE`xO9_tBZ@6lPJR$BH~B^8Z;>Gfvx#txgZCd&Yc_9yg)P7;9#g~ix?-q zj?)JYx(#~8u-_B?!25RQw{Z4K>?W&O#ue{h^R3snk^b^2TNrla8!jRn0ZRv&ck$dP z8$E6LNRkh5p1#wu8A{}xvx*$i@akvfP-ZC*a+dxuOGO-CP?O*3*igB>|JSL>ZfR-H zH#2`UJ9pIgb`dn@MV43va5bBt=hG8Djqx}HJS7w$_65EQAhL@}G)|%&KrsEDTObLI zbAJ|Mu|qhF69n=kiv~pk7EevNnLyLr@{Zn8MrU|`O9M9U?C$=vv~b&(n2aZ!E`&&* z-e_GQCHe=Va|nX#zI=z4lJVlL&Q`?Bz(DCh;BF)YU~b}=2%GBR!%MJ3aU-~qn=w-({@gX7#G%$WxT*3xvhtj*f%;0uqWtB zRoYuyU9t(uuXR@YN^ys=Bj9%K-m{0dUKraXh5(J&hgtr27fZr%i_qwPBvzJSB+ikw zSwbdJbno67+tzs9b&#k6{w1e3{$|;sV~kaQ0O*TXLl;A1%xi;kMi{QEKe!qiIxyo2 zVCspXh+_0SpfQYTIs&iXU?RyeYvuPFV+ye z@x}@OF<>E%;L5oKM$+HtS^wuRXoLxE!^w}8kZtOEbqG^Af(Y!r z@y;z7i^Vs9RCt`3nSb&CHZd$!8)BlOn2ci@G>;y&aCDc!SbfQsz!d0Z9mqklF8y@nR<-99`f-evMYFecR2YCx1THc;QOOt9h(S8<8_l> zOp|K}?h+Pm9sh|XfG-6z*REW#_wMasU5n)Er$!TCxVTtZ_rq(Upj!?@t{s2Km>0|g z5LqQ9B)7gH?3KNQD_89#CGXz%g@G$fP}H14(c99J?>S?MJ^0C!eb0-|^6uWX1xR-UWD zV?(?gUyH5|CV+p^;*Vi&nJ0dZcYhVl`YaW>z2F`9>s7znf93;G$1D-Brm_MWq&tNpUI8l zG+>8wVn6mz4MrKy6~ zi1Bam4F}6To9U|d-<~=u19c$hOhRZ11nb=mw}1sewc0V+}mvrWhyWx4m$IJMWI2H7EjKh4RwUC6{$u zOS?bS@1#`xgoUL5Rur;>3Gy9z6}AyOG>8{V=()CJ7(6=p*igIjawBTS^@qri1E|~0 z%%%)Ss+klu2o+M^o`U+9A5gV5FGeIb#B|$x*rH@iDJ><7k#DS4zP@rS`X^5oXg+uN z;7taVd5t7ue(oCEZWrw#@Q#i56jxqt)O6zd)nk_%vH8GT|L-kh{1QG#ue7gtKdC(V zrLHEaSjynirAwP5UR%vive<$}$3`z&NTAsn=u*Rzw)Nc>;fsCFA>H;0&Q)KoCCT=Y}vIV$nI`|Ad9LmUWC5!b^@Ui;3=hn=;;oc$=<|GWgjk-< zQ0&gwzTpi*%! z>}GtoA7+Y0Pl3M0o2~KySrv}gVqy+*_OA6|+bW^-yL|aFW`mX%e>6RR_RQv4570)^ zp%<%hXha0D)LFh-B=vy@QZbYBO-l&J`t z;-8@#WFVUQgVyu=Q6k!p?n^xJYr2{Vq>dNn*g^XFBDD)MK&R{?cQdyDO@?I>mmRXl zs7}AdlY0+#)cZIXhMI4k-*-3sH7_BTUT?#A?DS)oPW`=0B_)py#;x!9NdPr}H-Sad z9TGwuFRz?u9-7qvY@*Tvz)MHC3Cbktvw9EF17=BXq$*ypPYrqX+)?5D8sffa{VcV9%Ca* z$&6C676zD^TM+kj_G5Hg0(3Irq3{7n?;JmU@`0Z41D%~LsD|?6Y17G);iXkLMN~lDPv0>{zyeX1v61IXPd9!#$3H-bHZxcI*xs zclSNW%ZnZ?!YfqprMsHie~u`)*KLeZekWTCRQtlWZ1EE`C-4TB7rwAEnd{~G>AiG) z`?1BPC7{TV=FPs{m1|{X^%BmmC;ZN#N5N+68aF6qW#sK$d@ZZXYv`SY?16!#Vg&5Kad07KV@cQ-c`5Nx-KQY-2?m=N`R@Xz5zb^o_gO9g2Y$xCJfBgv&dho}^VM!$D za18nJRi|R66h0VO?eK;`2`hRV@uw)vL}QtZFKp|epI;^)Q&| zAHD=^f#xX3=$S3vq&aE)eYF1|__?)PF5vk?0TdAtfl$7OZmH@ z##53AE3wW}Y2>6}?#}O15PSW(A23nC2v*|Fn{E@rG(`!}0YYyFPVNI9InvJ+)*pXv z7}UBudW<5%xTx{Z@876Li&1t#?+UY0wCkkTLD}N>V~bPV!69#~(C?f?PlH`m6Zw|t zr12gtwc{fG#XlYBF%}<1ES^zTu7(ptO6NF?SH+;JhZB%|{{0+1c5KUa{!d5H z-{OgNaG1nPq$m8o$e9>~0FDPjD*Vnz_MFxyCjM6ZZ?qmr1`ifN&^D+1NJ|KC4gAdT zf%E5k;{HhrJD4vlvaqJZ2bsdQwxl-}nmQg+ISivf71*}PI)8?9?kU_k%!F5( zt7&K?;Hd%>HPR5jBStOf1Cbu{U#t2?45-H6EG=AXnCxNSK<9{d4yOc``S#tr(iT;L znDkJ%r^s+CV9VIgdGPBfOXsY~=s&4tPOGzL*BdAN4C`75r_fh{8RTT3 zMVSFE7P>-E=mnk7lzbxgFa+OLe;|x214~#~;)8ed^DBLCyD3Zs(npMh>7E6TV<=?wErgrefbEAK-4n=dHQmZjRPPM(k^g^Cc^M?<| zRnTSD6k%ZGg$Z2bx5mCZyN`1+kmKCBf>X&_7)g|X*Ft9}0tfSp&h6j)7hXeAxMN2_ zBYCC`+{gkYB&aMwlv!Z6Yir>WhYyg^(a}-Z zvR-7LBvE+v2Rg#HQ1#*y5eujiskbVaa>eufCN|Hs)z15QrPiVU5E=oVoBi`hyF%=I z&>A+8E<0CO@$pss{K;Az7?ZxG4$=?%S$t#|5w@muh_+laB{x?y+W@8-`52_Zy8Yn6 z;RibTupu;fpkpJ((RCfG;p2=OU(PH%(i3){5;fWTzl-&`Wo*JlD$>q*fJ0SJSRdFe zyjb>#YzTuZGf*of0x3>V-r$?LxatK_!l?fX3j0KlVst(rF=Et)CMFsP0}EHK zEwGOOsDCj5YxKKDs$K+eXB)grKQ5^2=(s1o+L^0_CJ)CqSOg`~qxU^+ZK-WzhUdr4 zT(z{A*JZ-X2?jrSnlKP?u5IqA_fn82ha9<~bF^UE5qqVT)lihEjYK?}Rh%_ivD2pz zo8Dh=R*ZuH{mej1ea!&j4{%=IX@siY;KXjbXGXQliu`aq7$?G#medJ0=F*ohyXrQJ zFuty-@$&J($iKq9sV-Te7_}~Aa3bg*E%!Q%Q98-xYe1%QW zH@1RHqEI|HkVl4M<1)~D3WXRrO8yN%Rcp|)QP8xJ?LUPwEkM3$c-LrBe^OCA??CcZ z8*-YeZ!3=Xw{Ka=BaZ)|5iOyoM%j}&R2Az{AtWYA_*8$+uPr9ODL}P?++xVzfl~ed qas*~L+W*%P{NMlf|M8dKtWby9p5z+3ZO})4=h4F_RC1KeFaLkc*UYE@ diff --git a/doc/surya/surya_graph/surya_graph_DocumentEngineBase.sol.png b/doc/surya/surya_graph/surya_graph_DocumentEngineBase.sol.png index 4e185bef45fb6a419ea8a2a5d00f8ebed7d9781b..3d48907d967e545a5b055ec7d45080721f35e111 100644 GIT binary patch delta 139285 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

eJ1yAKFf4nhS7p+5k_$ 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%0DJ1m)1Pe#}^q8IWRTu4%Qg$)ITczCGlo*9%H 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

rt>*wRjm!<5^9vuz1HX>80&UM*6t4*YzmAXXY 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 bgh^HE@U!o=XpWF%!5>u>wL@tKF1Y^(B=io{ delta 25625 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 diff --git a/doc/surya/surya_report/surya_report_DocumentEngine.sol.md b/doc/surya/surya_report/surya_report_DocumentEngine.sol.md index ed1d0bc..6192354 100644 --- a/doc/surya/surya_report/surya_report_DocumentEngine.sol.md +++ b/doc/surya/surya_report/surya_report_DocumentEngine.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| ./DocumentEngine.sol | d031f83714aa8dd23df6cc3fea3071829fcacf82 | +| ./DocumentEngine.sol | b5712711a9a13e254b60044029edb4b66fc06213 | ### Contracts Description Table @@ -17,9 +17,9 @@ |||||| | **DocumentEngine** | Implementation | TokenBindingModule, VersionModule, AccessControlEnumerable, ERC2771Context ||| | └ | | Public ❗️ | 🛑 | ERC2771Context | -| └ | _authorizeDocumentManagement | Internal 🔒 | | | | └ | hasRole | Public ❗️ | |NO❗️ | | └ | supportsInterface | Public ❗️ | |NO❗️ | +| └ | _authorizeDocumentManagement | Internal 🔒 | | | | └ | _msgSender | Internal 🔒 | | | | └ | _msgData | Internal 🔒 | | | | └ | _contextSuffixLength | Internal 🔒 | | | diff --git a/doc/surya/surya_report/surya_report_DocumentEngineBase.sol.md b/doc/surya/surya_report/surya_report_DocumentEngineBase.sol.md index 7569255..4127471 100644 --- a/doc/surya/surya_report/surya_report_DocumentEngineBase.sol.md +++ b/doc/surya/surya_report/surya_report_DocumentEngineBase.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| ./DocumentEngineBase.sol | 30ea1b25c7af9e478f0dbb2b1e984672c10e5a39 | +| ./DocumentEngineBase.sol | d3de1fa1ff079856dc1cfe0f19ee8206e99a1fb1 | ### Contracts Description Table @@ -16,9 +16,6 @@ | └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | |||||| | **DocumentEngineBase** | Implementation | IERC1643, IERC1643MultiDocument, DocumentEngineInvariant, Context ||| -| └ | _authorizeDocumentManagement | Internal 🔒 | | | -| └ | _authorizeBoundTokenDocumentManagement | Internal 🔒 | | | -| └ | setDocument | Public ❗️ | 🛑 | onlyDocumentManager | | └ | removeDocument | External ❗️ | 🛑 | onlyDocumentManager | | └ | setDocument | External ❗️ | 🛑 | onlyBoundToken | | └ | removeDocument | External ❗️ | 🛑 | onlyBoundToken | @@ -30,10 +27,13 @@ | └ | getDocument | External ❗️ | |NO❗️ | | └ | getAllDocuments | External ❗️ | |NO❗️ | | └ | getAllDocuments | External ❗️ | |NO❗️ | -| └ | _getDocument | Internal 🔒 | | | +| └ | setDocument | Public ❗️ | 🛑 | onlyDocumentManager | | └ | _removeDocumentName | Internal 🔒 | 🛑 | | | └ | _removeDocument | Internal 🔒 | 🛑 | | | └ | _setDocument | Internal 🔒 | 🛑 | | +| └ | _authorizeDocumentManagement | Internal 🔒 | | | +| └ | _authorizeBoundTokenDocumentManagement | Internal 🔒 | | | +| └ | _getDocument | Internal 🔒 | | | ### Legend diff --git a/doc/surya/surya_report/surya_report_DocumentEngineOwnable.sol.md b/doc/surya/surya_report/surya_report_DocumentEngineOwnable.sol.md index 0623991..63f8872 100644 --- a/doc/surya/surya_report/surya_report_DocumentEngineOwnable.sol.md +++ b/doc/surya/surya_report/surya_report_DocumentEngineOwnable.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| ./DocumentEngineOwnable.sol | fd190b2e8e884a3940406385ad7cdd22381304f5 | +| ./DocumentEngineOwnable.sol | 7604130e6a1c6390643293f8afba1c3b49293244 | ### Contracts Description Table @@ -17,8 +17,8 @@ |||||| | **DocumentEngineOwnable** | Implementation | TokenBindingModule, VersionModule, Ownable2Step, ERC2771Context ||| | └ | | Public ❗️ | 🛑 | Ownable ERC2771Context | -| └ | _authorizeDocumentManagement | Internal 🔒 | | | | └ | supportsInterface | Public ❗️ | |NO❗️ | +| └ | _authorizeDocumentManagement | Internal 🔒 | | | | └ | _msgSender | Internal 🔒 | | | | └ | _msgData | Internal 🔒 | | | | └ | _contextSuffixLength | Internal 🔒 | | | diff --git a/doc/surya/surya_report/surya_report_IERC1643MultiDocument.sol.md b/doc/surya/surya_report/surya_report_IERC1643MultiDocument.sol.md index 603b9a2..6f4378a 100644 --- a/doc/surya/surya_report/surya_report_IERC1643MultiDocument.sol.md +++ b/doc/surya/surya_report/surya_report_IERC1643MultiDocument.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| ./interfaces/IERC1643MultiDocument.sol | 1837f12f2b88fb4a93bb0100608e8e380c75582a | +| ./interfaces/IERC1643MultiDocument.sol | 2ddea154d4764f7f0edb35a85dc8df6db72604e0 | ### Contracts Description Table @@ -16,10 +16,10 @@ | └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | |||||| | **IERC1643MultiDocument** | Interface | ||| -| └ | getDocument | External ❗️ | |NO❗️ | -| └ | getAllDocuments | External ❗️ | |NO❗️ | | └ | setDocument | External ❗️ | 🛑 |NO❗️ | | └ | removeDocument | External ❗️ | 🛑 |NO❗️ | +| └ | getDocument | External ❗️ | |NO❗️ | +| └ | getAllDocuments | External ❗️ | |NO❗️ | ### Legend diff --git a/doc/surya/surya_report/surya_report_ITokenBinding.sol.md b/doc/surya/surya_report/surya_report_ITokenBinding.sol.md index e9d97eb..d50a101 100644 --- a/doc/surya/surya_report/surya_report_ITokenBinding.sol.md +++ b/doc/surya/surya_report/surya_report_ITokenBinding.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| ./interfaces/ITokenBinding.sol | b1c29dd6482284b8dbdc9d725891ce63edd7093b | +| ./interfaces/ITokenBinding.sol | be03e0f7cda1b263ac7e94f6e57a9e23ad7ccc4a | ### Contracts Description Table diff --git a/doc/surya/surya_report/surya_report_TokenBindingModule.sol.md b/doc/surya/surya_report/surya_report_TokenBindingModule.sol.md index 3bfbeac..f0b2b23 100644 --- a/doc/surya/surya_report/surya_report_TokenBindingModule.sol.md +++ b/doc/surya/surya_report/surya_report_TokenBindingModule.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| ./modules/TokenBindingModule.sol | e6198bc6b9cc314809dde5b6d237afb82396f7a2 | +| ./modules/TokenBindingModule.sol | 5374117f2b0ecde1d5a590d6e30c74386de1a0fc | ### Contracts Description Table @@ -18,8 +18,8 @@ | **TokenBindingModule** | Implementation | DocumentEngineBase, ITokenBinding ||| | └ | bindToken | External ❗️ | 🛑 |NO❗️ | | └ | unbindToken | External ❗️ | 🛑 |NO❗️ | -| └ | _setTokenBinding | Internal 🔒 | 🛑 | | | └ | isTokenBound | Public ❗️ | |NO❗️ | +| └ | _setTokenBinding | Internal 🔒 | 🛑 | | | └ | _authorizeBoundTokenDocumentManagement | Internal 🔒 | | | | └ | _checkTokenBound | Internal 🔒 | | | diff --git a/doc/surya/surya_report/surya_report_VersionModule.sol.md b/doc/surya/surya_report/surya_report_VersionModule.sol.md index 96dc2da..b50d39c 100644 --- a/doc/surya/surya_report/surya_report_VersionModule.sol.md +++ b/doc/surya/surya_report/surya_report_VersionModule.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| ./modules/VersionModule.sol | 50457b3210365f6d56bfb89d8421dd75464409e2 | +| ./modules/VersionModule.sol | c57266064b38591fd3eda39669e5ce7e0ab759e8 | ### Contracts Description Table diff --git a/script/DeployDocumentEngine.s.sol b/script/DeployDocumentEngine.s.sol index f99b714..343ca9a 100644 --- a/script/DeployDocumentEngine.s.sol +++ b/script/DeployDocumentEngine.s.sol @@ -1,7 +1,7 @@ //SPDX-License-Identifier: MPL-2.0 pragma solidity ^0.8.24; -import "forge-std/Script.sol"; +import {Script, console2} from "forge-std/Script.sol"; import {DocumentEngine} from "../src/DocumentEngine.sol"; /** @@ -21,6 +21,10 @@ import {DocumentEngine} from "../src/DocumentEngine.sol"; * described in the Foundry Key Management documentation (getfoundry.sh). */ contract DeployDocumentEngine is Script { + /** + * @notice Reads the deployment configuration from the environment and deploys the engine. + * @return documentEngine The freshly deployed {DocumentEngine}. + */ function run() external returns (DocumentEngine documentEngine) { address admin = vm.envOr("DOCUMENT_ENGINE_ADMIN", msg.sender); address forwarder = vm.envOr("DOCUMENT_ENGINE_FORWARDER", address(0)); @@ -33,7 +37,13 @@ contract DeployDocumentEngine is Script { console2.log(" version :", documentEngine.version()); } - /// @dev Broadcasted deployment, isolated from env parsing so it can be reused/tested. + /** + * @notice Deploys the engine with an explicit configuration. + * @dev Broadcasted deployment, isolated from env parsing so it can be reused/tested. + * @param admin address granted `DEFAULT_ADMIN_ROLE` + * @param forwarder ERC-2771 trusted forwarder; `address(0)` disables gasless support + * @return documentEngine The freshly deployed {DocumentEngine}. + */ function deploy(address admin, address forwarder) public returns (DocumentEngine documentEngine) { vm.startBroadcast(); documentEngine = new DocumentEngine(admin, forwarder); diff --git a/script/DeployDocumentEngineOwnable.s.sol b/script/DeployDocumentEngineOwnable.s.sol index 67c9a2b..a1ef714 100644 --- a/script/DeployDocumentEngineOwnable.s.sol +++ b/script/DeployDocumentEngineOwnable.s.sol @@ -1,7 +1,7 @@ //SPDX-License-Identifier: MPL-2.0 pragma solidity ^0.8.24; -import "forge-std/Script.sol"; +import {Script, console2} from "forge-std/Script.sol"; import {DocumentEngineOwnable} from "../src/DocumentEngineOwnable.sol"; /** @@ -21,6 +21,10 @@ import {DocumentEngineOwnable} from "../src/DocumentEngineOwnable.sol"; * described in the Foundry Key Management documentation (getfoundry.sh). */ contract DeployDocumentEngineOwnable is Script { + /** + * @notice Reads the deployment configuration from the environment and deploys the engine. + * @return documentEngine The freshly deployed {DocumentEngineOwnable}. + */ function run() external returns (DocumentEngineOwnable documentEngine) { address owner = vm.envOr("DOCUMENT_ENGINE_OWNER", msg.sender); address forwarder = vm.envOr("DOCUMENT_ENGINE_FORWARDER", address(0)); @@ -33,7 +37,13 @@ contract DeployDocumentEngineOwnable is Script { console2.log(" version :", documentEngine.version()); } - /// @dev Broadcasted deployment, isolated from env parsing so it can be reused/tested. + /** + * @notice Deploys the engine with an explicit configuration. + * @dev Broadcasted deployment, isolated from env parsing so it can be reused/tested. + * @param owner initial owner of the contract + * @param forwarder ERC-2771 trusted forwarder; `address(0)` disables gasless support + * @return documentEngine The freshly deployed {DocumentEngineOwnable}. + */ function deploy(address owner, address forwarder) public returns (DocumentEngineOwnable documentEngine) { vm.startBroadcast(); documentEngine = new DocumentEngineOwnable(owner, forwarder); diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index 2eb6406..79b6c48 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -1,14 +1,16 @@ //SPDX-License-Identifier: MPL-2.0 pragma solidity ^0.8.24; -import "OZ/access/extensions/AccessControlEnumerable.sol"; +import {AccessControl} from "OZ/access/AccessControl.sol"; +import {AccessControlEnumerable} from "OZ/access/extensions/AccessControlEnumerable.sol"; import {IAccessControl} from "OZ/access/IAccessControl.sol"; +import {Context} from "OZ/utils/Context.sol"; +import {ERC2771Context} from "OZ/metatx/ERC2771Context.sol"; import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; import {IERC1643MultiDocument} from "./interfaces/IERC1643MultiDocument.sol"; import {ITokenBinding} from "./interfaces/ITokenBinding.sol"; -import "OZ/metatx/ERC2771Context.sol"; -import "./modules/TokenBindingModule.sol"; -import "./modules/VersionModule.sol"; +import {TokenBindingModule} from "./modules/TokenBindingModule.sol"; +import {VersionModule} from "./modules/VersionModule.sol"; /** * @title DocumentEngine @@ -22,12 +24,18 @@ import "./modules/VersionModule.sol"; * the ERC-2771 (gasless) meta-transaction support. */ contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnumerable, ERC2771Context { - // Role allowed to manage documents on behalf of any smart contract, and to - // bind/unbind tokens (admin path). Token binding uses the shared allowlist in - // {TokenBindingModule}, not a dedicated role. + /** + * @notice Role allowed to manage documents on behalf of any smart contract, and to + * bind/unbind tokens (admin path). + * @dev Token binding uses the shared allowlist in {TokenBindingModule}, not a dedicated role. + */ bytes32 public constant DOCUMENT_MANAGER_ROLE = keccak256("DOCUMENT_MANAGER_ROLE"); - // Constructor to initialize the admin role + /** + * @notice Deploys the engine and grants `admin` the default admin role. + * @param admin address granted `DEFAULT_ADMIN_ROLE`; must not be the null address + * @param forwarderIrrevocable address of the ERC-2771 forwarder (gasless support) + */ constructor(address admin, address forwarderIrrevocable) ERC2771Context(forwarderIrrevocable) { if (admin == address(0)) { revert AdminWithAddressZeroNotAllowed(); @@ -36,18 +44,11 @@ contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnume } /*////////////////////////////////////////////////////////////// - ACCESS CONTROL (implementation) + ACCESS CONTROL (public surface) //////////////////////////////////////////////////////////////*/ /** - * @dev Authorization for the admin document-management path. - * The caller must hold `DOCUMENT_MANAGER_ROLE`. Override to customize. - */ - function _authorizeDocumentManagement() internal view virtual override { - _checkRole(DOCUMENT_MANAGER_ROLE); - } - - /** + * @notice Returns whether `account` holds `role`. * @dev Returns `true` if `account` has been granted `role`. The default admin * (`DEFAULT_ADMIN_ROLE`) is treated as holding **every** role. * @@ -55,6 +56,9 @@ contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnume * {AccessControlEnumerable} enumeration. `getRoleMember` / `getRoleMemberCount` * report only explicit grants, so a `DEFAULT_ADMIN_ROLE` holder satisfies * `hasRole(anyRole, admin)` yet does not appear in `getRoleMember(anyRole, ...)`. + * @param role The role identifier to check. + * @param account The account to check. + * @return True when `account` holds `role`, or holds `DEFAULT_ADMIN_ROLE`. */ function hasRole(bytes32 role, address account) public @@ -70,7 +74,12 @@ contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnume return super.hasRole(role, account); } + /*////////////////////////////////////////////////////////////// + ERC165 + //////////////////////////////////////////////////////////////*/ + /** + * @notice Returns whether this contract implements `interfaceId`. * @dev ERC-165 discovery: advertises ERC-1643 and its multi-subject extension, the token-binding * surface, the version module (ERC-8303) and `AccessControlEnumerable`. * @@ -89,6 +98,9 @@ contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnume * document consumers at the **subject**, or use the address-scoped `getDocument(subject, name)`. * * See {IERC165-supportsInterface}. + * @param interfaceId The ERC-165 interface identifier to query. + * @return True when `interfaceId` is one of the advertised interfaces or is supported by a base + * contract. */ function supportsInterface(bytes4 interfaceId) public @@ -101,12 +113,26 @@ contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnume || interfaceId == type(ITokenBinding).interfaceId || super.supportsInterface(interfaceId); } + /*////////////////////////////////////////////////////////////// + ACCESS CONTROL (implementation) + //////////////////////////////////////////////////////////////*/ + + /** + * @dev Authorization for the admin document-management path. + * The caller must hold `DOCUMENT_MANAGER_ROLE`. Override to customize. + */ + function _authorizeDocumentManagement() internal view virtual override { + _checkRole(DOCUMENT_MANAGER_ROLE); + } + /*////////////////////////////////////////////////////////////// ERC2771 //////////////////////////////////////////////////////////////*/ /** * @dev This surcharge is not necessary if you do not use ERC2771 + * @return sender The transaction sender, unwrapped from the ERC-2771 calldata suffix when the + * call came through the trusted forwarder. */ function _msgSender() internal view override(ERC2771Context, Context) returns (address sender) { return ERC2771Context._msgSender(); @@ -114,6 +140,8 @@ contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnume /** * @dev This surcharge is not necessary if you do not use ERC2771 + * @return The calldata, stripped of the ERC-2771 sender suffix when the call came through the + * trusted forwarder. */ function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata) { return ERC2771Context._msgData(); @@ -121,6 +149,7 @@ contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnume /** * @dev This surcharge is not necessary if you do not use the MetaTxModule + * @return The length of the ERC-2771 calldata suffix holding the sender address. */ function _contextSuffixLength() internal view override(ERC2771Context, Context) returns (uint256) { return ERC2771Context._contextSuffixLength(); diff --git a/src/DocumentEngineBase.sol b/src/DocumentEngineBase.sol index 6dd9f4c..1eb97cc 100644 --- a/src/DocumentEngineBase.sol +++ b/src/DocumentEngineBase.sol @@ -1,10 +1,10 @@ //SPDX-License-Identifier: MPL-2.0 pragma solidity ^0.8.24; -import "OZ/utils/Context.sol"; -import "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; +import {Context} from "OZ/utils/Context.sol"; +import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; import {IERC1643MultiDocument} from "./interfaces/IERC1643MultiDocument.sol"; -import "./DocumentEngineInvariant.sol"; +import {DocumentEngineInvariant} from "./DocumentEngineInvariant.sol"; /** * @title DocumentEngineBase @@ -20,12 +20,18 @@ import "./DocumentEngineInvariant.sol"; * CMTAT and CMTA/RuleEngine pattern. */ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, DocumentEngineInvariant, Context { - // Mapping from contract addresses to document names to their corresponding Document structs + /** + * @notice Documents held for each subject, keyed by subject address then document name. + */ mapping(address => mapping(bytes32 => Document)) private _documents; + + /** + * @notice The names of every document currently tracked for each subject. + */ mapping(address => bytes32[]) private _documentNames; /*////////////////////////////////////////////////////////////// - ACCESS CONTROL (hooks) + ACCESS CONTROL (modifiers) //////////////////////////////////////////////////////////////*/ /** @@ -49,35 +55,14 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen _; } - /** - * @dev Authorization hook for the admin document-management path. - * Implemented by the deployment contract (e.g. a role check). - */ - function _authorizeDocumentManagement() internal view virtual; - - /** - * @dev Authorization hook for the bound-token document-management path. - * Implemented by the deployment contract (e.g. a role check). - */ - function _authorizeBoundTokenDocumentManagement() internal view virtual; - /*////////////////////////////////////////////////////////////// - PUBLIC/EXTERNAL FUNCTIONS + EXTERNAL FUNCTIONS //////////////////////////////////////////////////////////////*/ - /** - * @notice Restricted function to set or update a document - */ - function setDocument(address subject, bytes32 name_, string memory uri_, bytes32 documentHash_) - public - override - onlyDocumentManager - { - _setDocument(subject, name_, uri_, documentHash_); - } - /** * @notice Restricted function to remove a document for a given smart contract and name + * @param subject The contract the document belongs to. + * @param name_ The document name. */ function removeDocument(address subject, bytes32 name_) external override onlyDocumentManager { _removeDocument(subject, name_); @@ -92,6 +77,9 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen * this engine. How a token is bound is deployment-specific (see the * {_authorizeBoundTokenDocumentManagement} implementations). A bound token can * only manage its own documents; it can never affect another contract's documents. + * @param name_ The document name. + * @param uri_ The document location. + * @param documentHash_ The hash of the document contents. */ function setDocument(bytes32 name_, string calldata uri_, bytes32 documentHash_) external override onlyBoundToken { _setDocument(_msgSender(), name_, uri_, documentHash_); @@ -100,6 +88,7 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen /** * @notice ERC-1643 function to remove a document for the caller. * @dev See {setDocument}. Scoped to the caller (`_msgSender()`) namespace. + * @param name_ The document name. */ function removeDocument(bytes32 name_) external override onlyBoundToken { _removeDocument(_msgSender(), name_); @@ -107,6 +96,11 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen /** * @notice Batch version of setDocument to handle multiple documents at once + * @dev All-or-nothing: a single invalid entry reverts the whole batch. + * @param subjects The contract each document belongs to, one per entry. + * @param names The document names, one per entry. + * @param uris The document locations, one per entry. + * @param hashes The document content hashes, one per entry. */ function batchSetDocuments( address[] calldata subjects, @@ -128,6 +122,11 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen /** * @notice Batch version of setDocument to handle multiple documents at once + * @dev All-or-nothing: a single invalid entry reverts the whole batch. + * @param subject The contract every document in the batch belongs to. + * @param names The document names, one per entry. + * @param uris The document locations, one per entry. + * @param hashes The document content hashes, one per entry. */ function batchSetDocuments( address subject, @@ -146,6 +145,9 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen /** * @notice Batch version of removeDocument to handle multiple documents at once + * @dev All-or-nothing: a single missing document reverts the whole batch. + * @param subjects The contract each document belongs to, one per entry. + * @param names The document names, one per entry. */ function batchRemoveDocuments(address[] calldata subjects, bytes32[] calldata names) external onlyDocumentManager { if (subjects.length == 0 || (subjects.length != names.length)) { @@ -160,6 +162,9 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen /** * @notice Batch version of removeDocument to handle multiple documents at once + * @dev All-or-nothing: a single missing document reverts the whole batch. + * @param subject The contract every document in the batch belongs to. + * @param names The document names, one per entry. */ function batchRemoveDocuments(address subject, bytes32[] calldata names) external onlyDocumentManager { if (names.length == 0) { @@ -177,6 +182,10 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen * @dev Returns the three fields as flat values, matching the ERC-1643 ABI. The `Document` * struct is kept for storage only: returning it would prepend a struct offset word to the * returndata, so a consumer decoding per the ERC-1643 signature would silently mis-decode. + * @param name_ The document name. + * @return uri Document location. + * @return documentHash Hash of the document contents. + * @return lastModified Last update timestamp. */ function getDocument(bytes32 name_) external @@ -190,6 +199,11 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen /** * @notice Public function to get a document for a specific contract address * @dev Flat return, see {getDocument(bytes32)}. + * @param subject The contract the document belongs to. + * @param name_ The document name. + * @return uri Document location. + * @return documentHash Hash of the document contents. + * @return lastModified Last update timestamp. */ function getDocument(address subject, bytes32 name_) external @@ -202,6 +216,7 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen /** * @notice Get all document names for msg.sender + * @return The names of every document currently tracked for the caller. */ function getAllDocuments() external view override returns (bytes32[] memory) { return _documentNames[_msgSender()]; @@ -209,29 +224,40 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen /** * @notice Get all document names for a specific smart contract + * @param subject The contract to enumerate documents for. + * @return The names of every document currently tracked for `subject`. */ function getAllDocuments(address subject) external view override returns (bytes32[] memory) { return _documentNames[subject]; } /*////////////////////////////////////////////////////////////// - INTERNAL FUNCTIONS + PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////*/ /** - * @dev Internal function to fetch a document, as flat values + * @notice Restricted function to set or update a document + * @param subject The contract the document belongs to. + * @param name_ The document name. + * @param uri_ The document location. + * @param documentHash_ The hash of the document contents. */ - function _getDocument(address subject, bytes32 name_) - internal - view - returns (string memory uri, bytes32 documentHash, uint256 lastModified) + function setDocument(address subject, bytes32 name_, string memory uri_, bytes32 documentHash_) + public + override + onlyDocumentManager { - Document storage doc = _documents[subject][name_]; - return (doc.uri, doc.documentHash, doc.lastModified); + _setDocument(subject, name_, uri_, documentHash_); } + /*////////////////////////////////////////////////////////////// + INTERNAL FUNCTIONS + //////////////////////////////////////////////////////////////*/ + /** * @dev Internal helper to remove the document name from the list of document names + * @param subject The contract the document belongs to. + * @param name_ The document name to remove from the list. */ function _removeDocumentName(address subject, bytes32 name_) internal { uint256 length = _documentNames[subject].length; @@ -244,6 +270,12 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen } } + /** + * @dev Shared removal implementation: reverts {ERC1643MissingDocument} when the document does + * not exist, then emits the address-carrying extension event and clears the entry. + * @param subject The contract the document belongs to. + * @param name_ The document name. + */ function _removeDocument(address subject, bytes32 name_) internal { Document memory doc = _documents[subject][name_]; // ERC-1643: reverts when the named document does not exist @@ -261,6 +293,14 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen _removeDocumentName(subject, name_); } + /** + * @dev Shared create/update implementation: rejects a null `subject` and a null `name_`, tracks + * the name on first write, then stores the document and emits the extension event. + * @param subject The contract the document belongs to. + * @param name_ The document name. + * @param uri_ The document location. + * @param documentHash_ The hash of the document contents. + */ function _setDocument(address subject, bytes32 name_, string memory uri_, bytes32 documentHash_) internal { // Multi-token guard: `subject` must be a real contract address, never the // null namespace. (The bound-token path passes `_msgSender()`, never zero.) @@ -285,4 +325,37 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen // event (see {_removeDocument} note and doc/ERCSpecification). emit DocumentUpdatedForSubject(subject, name_, uri_, documentHash_); } + + /*////////////////////////////////////////////////////////////// + ACCESS CONTROL (hooks) + //////////////////////////////////////////////////////////////*/ + + /** + * @dev Authorization hook for the admin document-management path. + * Implemented by the deployment contract (e.g. a role check). + */ + function _authorizeDocumentManagement() internal view virtual; + + /** + * @dev Authorization hook for the bound-token document-management path. + * Implemented by the deployment contract (e.g. a role check). + */ + function _authorizeBoundTokenDocumentManagement() internal view virtual; + + /** + * @dev Internal function to fetch a document, as flat values + * @param subject The contract the document belongs to. + * @param name_ The document name. + * @return uri Document location. + * @return documentHash Hash of the document contents. + * @return lastModified Last update timestamp. + */ + function _getDocument(address subject, bytes32 name_) + internal + view + returns (string memory uri, bytes32 documentHash, uint256 lastModified) + { + Document storage doc = _documents[subject][name_]; + return (doc.uri, doc.documentHash, doc.lastModified); + } } diff --git a/src/DocumentEngineOwnable.sol b/src/DocumentEngineOwnable.sol index 4a6ca1c..30666cd 100644 --- a/src/DocumentEngineOwnable.sol +++ b/src/DocumentEngineOwnable.sol @@ -3,12 +3,13 @@ pragma solidity ^0.8.24; import {Ownable} from "OZ/access/Ownable.sol"; import {Ownable2Step} from "OZ/access/Ownable2Step.sol"; +import {Context} from "OZ/utils/Context.sol"; +import {ERC2771Context} from "OZ/metatx/ERC2771Context.sol"; import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol"; import {IERC1643MultiDocument} from "./interfaces/IERC1643MultiDocument.sol"; import {ITokenBinding} from "./interfaces/ITokenBinding.sol"; -import "OZ/metatx/ERC2771Context.sol"; -import "./modules/TokenBindingModule.sol"; -import "./modules/VersionModule.sol"; +import {TokenBindingModule} from "./modules/TokenBindingModule.sol"; +import {VersionModule} from "./modules/VersionModule.sol"; /** * @title DocumentEngineOwnable @@ -23,40 +24,51 @@ import "./modules/VersionModule.sol"; */ contract DocumentEngineOwnable is TokenBindingModule, VersionModule, Ownable2Step, ERC2771Context { /** + * @notice Deploys the engine with `owner_` as its single privileged account. * @param owner_ initial owner of the contract * @param forwarderIrrevocable address of the ERC-2771 forwarder (gasless support) */ constructor(address owner_, address forwarderIrrevocable) Ownable(owner_) ERC2771Context(forwarderIrrevocable) {} /*////////////////////////////////////////////////////////////// - ACCESS CONTROL (implementation) + ERC165 //////////////////////////////////////////////////////////////*/ /** - * @dev Authorization for the admin document-management path (and, via - * {TokenBindingModule}, for token binding): only the owner. - */ - function _authorizeDocumentManagement() internal view virtual override { - _checkOwner(); - } - - /** + * @notice Returns whether this contract implements `interfaceId`. * @dev ERC-165 discovery: advertises ERC-1643 and its multi-subject extension, the token-binding * surface and the version module (ERC-8303). See the rationale on * {DocumentEngine-supportsInterface} for what `type(IERC1643).interfaceId` does and does not * tell a caller here. See {IERC165-supportsInterface}. + * @param interfaceId The ERC-165 interface identifier to query. + * @return True when `interfaceId` is one of the advertised interfaces or is supported by a base + * contract. */ function supportsInterface(bytes4 interfaceId) public view virtual override(VersionModule) returns (bool) { return interfaceId == type(IERC1643).interfaceId || interfaceId == type(IERC1643MultiDocument).interfaceId || interfaceId == type(ITokenBinding).interfaceId || super.supportsInterface(interfaceId); } + /*////////////////////////////////////////////////////////////// + ACCESS CONTROL (implementation) + //////////////////////////////////////////////////////////////*/ + + /** + * @dev Authorization for the admin document-management path (and, via + * {TokenBindingModule}, for token binding): only the owner. + */ + function _authorizeDocumentManagement() internal view virtual override { + _checkOwner(); + } + /*////////////////////////////////////////////////////////////// ERC2771 //////////////////////////////////////////////////////////////*/ /** * @dev This surcharge is not necessary if you do not use ERC2771 + * @return sender The transaction sender, unwrapped from the ERC-2771 calldata suffix when the + * call came through the trusted forwarder. */ function _msgSender() internal view override(ERC2771Context, Context) returns (address sender) { return ERC2771Context._msgSender(); @@ -64,6 +76,8 @@ contract DocumentEngineOwnable is TokenBindingModule, VersionModule, Ownable2Ste /** * @dev This surcharge is not necessary if you do not use ERC2771 + * @return The calldata, stripped of the ERC-2771 sender suffix when the call came through the + * trusted forwarder. */ function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata) { return ERC2771Context._msgData(); @@ -71,6 +85,7 @@ contract DocumentEngineOwnable is TokenBindingModule, VersionModule, Ownable2Ste /** * @dev This surcharge is not necessary if you do not use the MetaTxModule + * @return The length of the ERC-2771 calldata suffix holding the sender address. */ function _contextSuffixLength() internal view override(ERC2771Context, Context) returns (uint256) { return ERC2771Context._contextSuffixLength(); diff --git a/src/interfaces/IERC1643MultiDocument.sol b/src/interfaces/IERC1643MultiDocument.sol index b6d0349..89e555c 100644 --- a/src/interfaces/IERC1643MultiDocument.sol +++ b/src/interfaces/IERC1643MultiDocument.sol @@ -13,40 +13,72 @@ pragma solidity ^0.8.24; * on-chain product). See `doc/ERCSpecification/erc-draft_multi_document_management.md`. */ interface IERC1643MultiDocument { - /// @notice Reverts when `setDocument` or `removeDocument` is called with `subject == address(0)`. - /// @dev Specific to this proposal; it has no ERC-1643 counterpart, because ERC-1643's - /// `setDocument` has no `subject` argument — its subject is implicitly the contract itself, - /// which is never the null address. Named after the proposal that defines the condition, not - /// after one in which the condition cannot occur; the two errors this interface shares with - /// ERC-1643 (`ERC1643InvalidName`, `ERC1643MissingDocument`) keep their prefix for the opposite - /// reason, and are declared by `IERC1643`, never here. - error MultiDocumentInvalidSubject(); + /** + * @notice Emitted when a document is created or updated for `subject`. + * @param subject The contract the document belongs to. + * @param name The document name. + * @param uri The document location. + * @param documentHash The hash of the document contents. + */ + event DocumentUpdatedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); - /// @notice Returns metadata for the document `name` belonging to `subject`. - /// @dev Returns the three fields as flat values, matching the specification ABI. A missing - /// document yields empty values (`""`, `bytes32(0)`, `0`) and does not revert. - /// @return uri Document location. - /// @return documentHash Hash of the document contents. - /// @return lastModified Last update timestamp. - function getDocument(address subject, bytes32 name) - external - view - returns (string memory uri, bytes32 documentHash, uint256 lastModified); + /** + * @notice Emitted when a document is removed for `subject`. + * @param subject The contract the document belonged to. + * @param name The document name. + * @param uri The document location as it was before removal. + * @param documentHash The hash of the document contents as it was before removal. + */ + event DocumentRemovedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); - /// @notice Returns all document names currently tracked for `subject`. - function getAllDocuments(address subject) external view returns (bytes32[] memory documentNames); + /** + * @notice Reverts when `setDocument` or `removeDocument` is called with `subject == address(0)`. + * @dev Specific to this proposal; it has no ERC-1643 counterpart, because ERC-1643's + * `setDocument` has no `subject` argument — its subject is implicitly the contract itself, + * which is never the null address. Named after the proposal that defines the condition, not + * after one in which the condition cannot occur; the two errors this interface shares with + * ERC-1643 (`ERC1643InvalidName`, `ERC1643MissingDocument`) keep their prefix for the opposite + * reason, and are declared by `IERC1643`, never here. + */ + error MultiDocumentInvalidSubject(); - /// @notice Creates or updates a document entry for `subject`. - /// @dev MUST emit {DocumentUpdatedForSubject} on success. + /** + * @notice Creates or updates a document entry for `subject`. + * @dev MUST emit {DocumentUpdatedForSubject} on success. + * @param subject The contract the document belongs to. + * @param name The document name. + * @param uri The document location. + * @param documentHash The hash of the document contents. + */ function setDocument(address subject, bytes32 name, string calldata uri, bytes32 documentHash) external; - /// @notice Removes an existing document entry for `subject`. - /// @dev MUST emit {DocumentRemovedForSubject} on success. + /** + * @notice Removes an existing document entry for `subject`. + * @dev MUST emit {DocumentRemovedForSubject} on success. + * @param subject The contract the document belongs to. + * @param name The document name. + */ function removeDocument(address subject, bytes32 name) external; - /// @notice Emitted when a document is created or updated for `subject`. - event DocumentUpdatedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); + /** + * @notice Returns metadata for the document `name` belonging to `subject`. + * @dev Returns the three fields as flat values, matching the specification ABI. A missing + * document yields empty values (`""`, `bytes32(0)`, `0`) and does not revert. + * @param subject The contract the document belongs to. + * @param name The document name. + * @return uri Document location. + * @return documentHash Hash of the document contents. + * @return lastModified Last update timestamp. + */ + function getDocument(address subject, bytes32 name) + external + view + returns (string memory uri, bytes32 documentHash, uint256 lastModified); - /// @notice Emitted when a document is removed for `subject`. - event DocumentRemovedForSubject(address indexed subject, bytes32 indexed name, string uri, bytes32 documentHash); + /** + * @notice Returns all document names currently tracked for `subject`. + * @param subject The contract to enumerate documents for. + * @return documentNames The names of every document currently tracked for `subject`. + */ + function getAllDocuments(address subject) external view returns (bytes32[] memory documentNames); } diff --git a/src/interfaces/ITokenBinding.sol b/src/interfaces/ITokenBinding.sol index b9c46b9..13dd1d8 100644 --- a/src/interfaces/ITokenBinding.sol +++ b/src/interfaces/ITokenBinding.sol @@ -12,24 +12,40 @@ pragma solidity ^0.8.24; * revert raised when a non-bound caller attempts a write are deployment-specific. */ interface ITokenBinding { - /// @notice Emitted when a token is bound (`bound = true`) or unbound (`bound = false`). - /// @dev Emitted only when the binding actually changes, so the event stream contains no - /// no-op entries and an indexer can replay it as a sequence of transitions. + /** + * @notice Emitted when a token is bound (`bound = true`) or unbound (`bound = false`). + * @dev Emitted only when the binding actually changes, so the event stream contains no + * no-op entries and an indexer can replay it as a sequence of transitions. + * @param token The token whose binding changed. + * @param bound The new binding state: `true` when bound, `false` when unbound. + */ event TokenBindingSet(address indexed token, bool bound); - /// @notice Thrown when a binding operation targets the null address. + /** + * @notice Thrown when a binding operation targets the null address. + */ error TokenBindingInvalidToken(); - /// @notice Binds `token`, allowing it to manage its own documents. - /// @dev Idempotent: binding an already-bound token succeeds and emits nothing. - /// Reverts {TokenBindingInvalidToken} when `token` is the null address. + /** + * @notice Binds `token`, allowing it to manage its own documents. + * @dev Idempotent: binding an already-bound token succeeds and emits nothing. + * Reverts {TokenBindingInvalidToken} when `token` is the null address. + * @param token The token to bind. + */ function bindToken(address token) external; - /// @notice Unbinds `token`. - /// @dev Idempotent: unbinding a token that is not bound succeeds and emits nothing. - /// Reverts {TokenBindingInvalidToken} when `token` is the null address. + /** + * @notice Unbinds `token`. + * @dev Idempotent: unbinding a token that is not bound succeeds and emits nothing. + * Reverts {TokenBindingInvalidToken} when `token` is the null address. + * @param token The token to unbind. + */ function unbindToken(address token) external; - /// @notice Returns whether `token` is currently bound. + /** + * @notice Returns whether `token` is currently bound. + * @param token The token to query. + * @return True when `token` is bound, false otherwise. + */ function isTokenBound(address token) external view returns (bool); } diff --git a/src/modules/TokenBindingModule.sol b/src/modules/TokenBindingModule.sol index dfc86e3..bb5e56f 100644 --- a/src/modules/TokenBindingModule.sol +++ b/src/modules/TokenBindingModule.sol @@ -44,6 +44,13 @@ abstract contract TokenBindingModule is DocumentEngineBase, ITokenBinding { _setTokenBinding(token, false); } + /** + * @inheritdoc ITokenBinding + */ + function isTokenBound(address token) public view virtual override returns (bool) { + return _boundTokens[token]; + } + /** * @dev Shared bind/unbind implementation. * @@ -55,6 +62,8 @@ abstract contract TokenBindingModule is DocumentEngineBase, ITokenBinding { * event stream free of no-op entries, so an indexer can treat every {TokenBindingSet} as a real * transition rather than having to de-duplicate. The repeated call still succeeds, since the * caller's intent — "this token is (not) bound" — already holds. + * @param token The token whose binding is being set. + * @param bound The binding state to apply: `true` to bind, `false` to unbind. */ function _setTokenBinding(address token, bool bound) internal { if (token == address(0)) { @@ -67,11 +76,6 @@ abstract contract TokenBindingModule is DocumentEngineBase, ITokenBinding { emit TokenBindingSet(token, bound); } - /// @inheritdoc ITokenBinding - function isTokenBound(address token) public view virtual override returns (bool) { - return _boundTokens[token]; - } - /** * @dev Bound-token document-management authorization: the caller * (`_msgSender()`) must be a bound token. diff --git a/src/modules/VersionModule.sol b/src/modules/VersionModule.sol index e331d0f..68b561c 100644 --- a/src/modules/VersionModule.sol +++ b/src/modules/VersionModule.sol @@ -28,8 +28,11 @@ abstract contract VersionModule is IERC8303, ERC165 { } /** + * @notice Returns whether this contract implements `interfaceId`. * @dev Advertises ERC-8303 support (interface id `0x54fd4d50`). * See {IERC165-supportsInterface}. + * @param interfaceId The ERC-165 interface identifier to query. + * @return True when `interfaceId` is ERC-8303 or is supported by a base contract. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC8303).interfaceId || super.supportsInterface(interfaceId); From a2f27499ef29b39859aaf38ba40c68ea2805982c Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:35:33 +0200 Subject: [PATCH 31/47] perf: cache storage pointers in document removal (-2845 gas), document unrevokable admin role --- AGENTS.md | 4 + CHANGELOG.md | 19 ++ CLAUDE.md | 4 + doc/audits/AUDIT_OVERVIEW.md | 1 + doc/audits/CLAUDE_ANALYSIS.md | 483 ++++++++++++++++++++++++++++++++ src/DocumentEngine.sol | 9 + src/DocumentEngineBase.sol | 11 +- src/DocumentEngineInvariant.sol | 6 +- test/DocumentEngine.t.sol | 22 ++ 9 files changed, 553 insertions(+), 6 deletions(-) create mode 100644 doc/audits/CLAUDE_ANALYSIS.md diff --git a/AGENTS.md b/AGENTS.md index 0e7ccba..720b3be 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -160,6 +160,10 @@ Other important files: (0 High · 1 Med · 1 Low · 2 Info) were run for `v0.4.0` — nothing to fix in either. Slither's dependency filter must be `lib` (Foundry layout); `--filter-paths` fails open, so an entry matching nothing silently pulls the vendored tree into scope. + `doc/audits/CLAUDE_ANALYSIS.md` is the code-quality review (not a security audit) — read + its "left as is" rows before proposing an optimisation: `unchecked {++i}` (0 gas on solc + 0.8.34), `string calldata` on the admin `setDocument` (49 gas *worse*), and extracting the + duplicated ERC-2771 overrides (impossible — C3 linearization) are all measured dead ends. - `IMPROVEMENT.md` — the open items: deviations from the two ERC specifications, with severity, effort and a recommendation for each. Update it when an item is fixed (move the record to `CHANGELOG.md` and `doc/audits/AUDIT_OVERVIEW.md`) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8219a3..e6d0130 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,25 @@ for which CMTAT release each version of this engine is built against. - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) [`v3.0.0-rc5`](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) - `foundry.lock` now records every submodule by tag; all five entries had gone stale since `v0.3.0`. - **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) +- **Code-quality review** (`doc/audits/CLAUDE_ANALYSIS.md`) — 14 findings, none a vulnerability. + Four implemented: + - **Gas, `_removeDocumentName`**: the `_documentNames[subject]` mapping slot was re-hashed on every + loop iteration; cached as a storage pointer. Measured **−2200 gas** on a 20-entry full scan. + - **Gas, `_removeDocument`**: the whole `Document` (URI included) was copied to memory to be read + three times; now read through a storage pointer. A further **−645 gas**. Combined, removal is + **−2845 gas (−3.3 %)** worst case. The emit must stay ahead of the `delete` — verified by + mutating the order and confirming `testRemoveDocumentEmitsForSubjectEvent` fails. + - **`hasRole` NatSpec**: documented that a role is **unrevokable from the default admin** — + `revokeRole` succeeds, emits `RoleRevoked` and drops `getRoleMemberCount`, yet the admin keeps the + access. Not a privilege issue (an admin can re-grant itself anything) but the call misreports. + Pinned by the new `testRevokingRoleFromDefaultAdminDoesNotRemoveAccess`. + - **`DocumentEngineInvariant`**: the error-location comment misattributed `NotBoundToken(address)` + to `ITokenBinding`; it is declared by `TokenBindingModule`. + + Notable non-changes, recorded so they are not re-raised: `unchecked { ++i }` buys **0 gas** on solc + 0.8.34 (measured); `string calldata` on the admin `setDocument` is **49 gas worse** than `memory` + (measured); and the duplicated ERC-2771 context overrides **cannot** be extracted into a shared + module — C3 linearization forces each deployment to re-state them, proven by compiler error. - **Style pass across `src/` and `script/` — behaviour-preserving.** Brought the sources in line with the Solidity style guide: functions reordered by visibility group (external → public → internal, `view`/`pure` last within each), so the `_authorize*` hooks and the ERC-2771 context overrides now diff --git a/CLAUDE.md b/CLAUDE.md index 0e7ccba..720b3be 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -160,6 +160,10 @@ Other important files: (0 High · 1 Med · 1 Low · 2 Info) were run for `v0.4.0` — nothing to fix in either. Slither's dependency filter must be `lib` (Foundry layout); `--filter-paths` fails open, so an entry matching nothing silently pulls the vendored tree into scope. + `doc/audits/CLAUDE_ANALYSIS.md` is the code-quality review (not a security audit) — read + its "left as is" rows before proposing an optimisation: `unchecked {++i}` (0 gas on solc + 0.8.34), `string calldata` on the admin `setDocument` (49 gas *worse*), and extracting the + duplicated ERC-2771 overrides (impossible — C3 linearization) are all measured dead ends. - `IMPROVEMENT.md` — the open items: deviations from the two ERC specifications, with severity, effort and a recommendation for each. Update it when an item is fixed (move the record to `CHANGELOG.md` and `doc/audits/AUDIT_OVERVIEW.md`) diff --git a/doc/audits/AUDIT_OVERVIEW.md b/doc/audits/AUDIT_OVERVIEW.md index 89f728d..40b71ee 100644 --- a/doc/audits/AUDIT_OVERVIEW.md +++ b/doc/audits/AUDIT_OVERVIEW.md @@ -27,6 +27,7 @@ Out of scope: `lib/` (CMTAT, RuleEngine, OpenZeppelin — audited, or not, upstr | Aderyn `0.6.5` | `v0.4.0` | [report](./tools/v0.4.0/aderyn/aderyn-report.md) | [feedback](./tools/v0.4.0/aderyn/aderyn-report-feedback.md) | | Slither `0.11.5` | `v0.4.0` | [report](./tools/v0.4.0/slither/slither-report.md) | [feedback](./tools/v0.4.0/slither/slither-report-feedback.md) | | ERC conformance analysis (AI-assisted) | `v0.4.0` | open items: [`IMPROVEMENT.md`](../../IMPROVEMENT.md) | — | +| Code-quality review (AI-assisted) | `v0.4.0` | [`CLAUDE_ANALYSIS.md`](./CLAUDE_ANALYSIS.md) — 14 findings, **no vulnerability**; 4 implemented, 2 open decisions | — | Both tool runs are against CMTAT `v3.3.0-rc3` and OpenZeppelin `v5.7.0`, with mocks and tests excluded. diff --git a/doc/audits/CLAUDE_ANALYSIS.md b/doc/audits/CLAUDE_ANALYSIS.md new file mode 100644 index 0000000..1a9bbdf --- /dev/null +++ b/doc/audits/CLAUDE_ANALYSIS.md @@ -0,0 +1,483 @@ +# DocumentEngine — Code Quality Review + +| | | +| --- | --- | +| Scope | `src/` (9 files, 307 nSLOC) and `script/` (2 files). `lib/`, `test/` excluded except where cited as evidence. | +| Version | `v0.4.0` (unreleased) | +| Dependencies | CMTAT `v3.3.0-rc3`, OpenZeppelin `v5.7.0`, RuleEngine `v3.0.0-rc5` | +| Compiler | solc `0.8.34`, `evm_version = prague`, optimizer on (200 runs) | +| Date | 2026-08-17 | +| Produced with | Claude Code | + +> **This is a code-quality review, not a security audit.** Nothing in this report is a +> vulnerability. No finding lets an unauthorized party move value, bypass a restriction, or brick a +> contract. The one finding that *looks* like an access-control problem on first reading — **H-1**, +> revoking a role from the default admin does not remove its access — is analysed below and is not +> exploitable: the default admin can re-grant itself any role in the same transaction, so the +> "revocation" could never have been a durable restriction. Its defect is misleading feedback, not +> lost containment. +> +> For the static-analysis passes see [`AUDIT_OVERVIEW.md`](./AUDIT_OVERVIEW.md); both Aderyn and +> Slither found nothing to fix. This review covers what those tools structurally cannot see. + +## Disposition summary + +| ID | Finding | Outcome | Where | +| --- | --- | --- | --- | +| A-1 | `unchecked { ++i }` would buy nothing on this compiler | ⬜ left as is (anti-recommendation) | `DocumentEngineBase.sol:118,141,158,175,264` | +| A-2 | `string memory` on the admin `setDocument` — `calldata` is **slower** here | ⬜ left as is (measured) | `DocumentEngineBase.sol:245` | +| B-1 | Mapping slot re-hashed every iteration in `_removeDocumentName` | ✅ fixed, **−2200 gas** | `DocumentEngineBase.sol:263` | +| B-2 | `_removeDocument` copied the whole `Document` (incl. the URI) to memory | ✅ fixed, **−645 gas** | `DocumentEngineBase.sol:281` | +| C-1 | Every event has exactly one emit site | ⬜ nothing to do — verified good | — | +| C-2 | Trusted forwarder set at construction without an event | ⬜ left as is | `DocumentEngine.sol:40` | +| D-1 | ERC-2771 trio duplicated byte-for-byte across both deployments | ⬜ left as is — **extraction proven impossible** | `DocumentEngine.sol:137`, `DocumentEngineOwnable.sol:73` | +| E-1 | `virtual` coverage inconsistent between the two modules | ⚠️ decide — not implemented | `DocumentEngineBase.sol`, `TokenBindingModule.sol` | +| F-1 | ERC-165 interface IDs — no inherited-selector trap | ⬜ nothing to do — verified correct | `DocumentEngine.sol:115` | +| G-1 | `DocumentEngineInvariant` comment misattributes `NotBoundToken` | ✅ fixed | `DocumentEngineInvariant.sol` | +| G-2 | Contracts point at documentation paths that have already moved once | ⚠️ decide — not implemented | 3 sites | +| G-3 | NatSpec block-length distribution is healthy | ⬜ nothing to do — measured | — | +| H-1 | A role cannot be revoked from the default admin, but the call succeeds | ✅ documented + regression test | `DocumentEngine.sol:73` | +| H-2 | Caller-scoped reads return an empty namespace instead of reverting | ⬜ left as is — already documented and tested | `DocumentEngineBase.sol:186` | + +Rows: 14. Fixed: 4. Left deliberately: 8. Open decisions: 2. + +## Outstanding + +| ID | Item | Why it is still open | +| --- | --- | --- | +| E-1 | Make the document-management API and the binding internals `virtual` | Changes the extension surface the project commits to. Free at runtime (measured, 0 gas) but it is a design commitment, so it is the maintainer's call — see the two options in E-1. | +| G-2 | Remove the `doc/…` pointers baked into contract comments | Two of the three sites *lean* on the doc rather than merely citing it; removing the pointer alone would leave an incomplete warning. Needs a sentence written per site, which is an editorial decision. | + +--- + +## A. Loops and iteration + +### A-1. `unchecked { ++i }` would buy exactly nothing — do not add it + +`DocumentEngineBase.sol:118, 141, 158, 175, 264`. All five loops already use `++i` with a bound +read once into a local: + +```solidity +uint256 length = subjects.length; +for (uint256 i = 0; i < length; ++i) { +``` + +This project compiles with solc `0.8.34`. Since **0.8.22** the compiler elides the overflow check on +a loop counter it can prove bounded, so the `unchecked` block that reviewers habitually recommend is +dead weight. Measured rather than asserted — two contracts, each with a single function of the same +name, so selector-dispatch depth cannot skew the comparison: + +| variant | gas, 100 iterations | +| --- | --- | +| `++i` | 33 005 | +| `unchecked { ++i }` | 33 005 | +| **delta** | **0** | + +**Verdict: leave.** Recorded here specifically so the next review does not re-raise it. Adding the +`unchecked` block would cost three lines of noise and buy zero gas. + +### A-2. `string memory` on the admin `setDocument` — `calldata` is measurably *worse* + +`DocumentEngineBase.sol:245`: + +```solidity +function setDocument(address subject, bytes32 name_, string memory uri_, bytes32 documentHash_) + public override onlyDocumentManager +``` + +The standing advice is that an external-only entrypoint should take `calldata`. This function +qualifies — nothing calls it internally — and the interface it overrides already declares +`string calldata`. I toggled it to `external` + `string calldata` in place and re-ran the same +harness: + +| path | `string memory` | `string calldata` | delta | +| --- | --- | --- | --- | +| new document, short URI | 107 895 | 107 944 | **+49** | +| new document, long URI (105 chars) | 194 876 | 194 925 | **+49** | +| update, long URI | 105 500 | 105 549 | **+49** | + +Consistently **49 gas worse**, and flat in URI length — so it is not the copy. The reason the +expected saving does not materialise is that `_setDocument` takes `string memory`, so the +calldata→memory copy happens either way; `calldata` only moves it and adds offset handling. + +**Verdict: leave.** This is the case where the textbook optimization is a pessimisation. Changing +`_setDocument` to take `calldata` too is not possible — the bound-token path already passes +`calldata` there and the batch paths pass array elements, so the parameter must stay `memory` for +one of its callers regardless. + +## B. Storage reads + +### B-1. The mapping slot was re-hashed on every loop iteration — **fixed, −2200 gas** + +`DocumentEngineBase.sol:262`. Before: + +```solidity +uint256 length = _documentNames[subject].length; +for (uint256 i = 0; i < length; ++i) { + if (_documentNames[subject][i] == name_) { + _documentNames[subject][i] = _documentNames[subject][length - 1]; + _documentNames[subject].pop(); +``` + +Every `_documentNames[subject]` recomputes `keccak256(subject . slot)` — a hash per access, twice per +iteration on the comparison path. Caching the array as a storage pointer computes it once: + +```solidity +bytes32[] storage names = _documentNames[subject]; +uint256 length = names.length; +for (uint256 i = 0; i < length; ++i) { + if (names[i] == name_) { +``` + +Measured on a subject holding 20 documents, toggled in place, same harness both times: + +| case | before | after | delta | +| --- | --- | --- | --- | +| target at index 19 (full scan) | 87 250 | 85 050 | **−2200** | +| target at index 0 (early exit) | 28 397 | 28 002 | −395 | + +≈116 gas per iteration. This is the function `IMPROVEMENT.md` item 4 already flags as the O(n) +scalability hotspot, and `batchRemoveDocuments` compounds it to O(n·m), so the saving multiplies. + +**Verdict: implemented.** Note this does not change the complexity — it lowers the constant. The +index-mapping fix that would make removal O(1) remains the open item it was. + +### B-2. `_removeDocument` copied the entire struct, URI included — **fixed, −645 gas** + +`DocumentEngineBase.sol:281`. `Document memory doc = _documents[subject][name_];` copies all three +fields into memory, including the dynamic `uri` string, before the existence check. Both the check +and the event read the fields, but they can read them through a storage pointer instead: + +```solidity +Document storage doc = _documents[subject][name_]; +``` + +| case | before B-1 | after B-1 | after B-1+B-2 | total | +| --- | --- | --- | --- | --- | +| full scan | 87 250 | 85 050 | **84 405** | **−2845 (−3.3 %)** | +| early exit | 28 397 | 28 002 | **27 357** | **−1040 (−3.7 %)** | + +**The hazard, and how it was checked.** With a storage pointer the emit *must* stay before the +`delete`; move it after and the event silently logs an empty URI and a zero hash rather than +reverting. Rather than assume the suite catches that, I introduced the mutation deliberately: + +``` +[FAIL: DocumentRemovedForSubject != expected DocumentRemovedForSubject] + testRemoveDocumentEmitsForSubjectEvent() +``` + +The guard is real. Ordering restored, 73/73 passing. + +**Verdict: implemented.** Storage layout re-checked from the compiled artifacts afterwards +(`--extra-output storageLayout`, 5 non-empty entries per deployment, unchanged). + +## C. Events + +### C-1. Single emit site per event — verified, nothing to do + +The usual failure here is an event emitted from several places, so "every write emits" holds by +convention rather than structurally. Counted: + +| event | emit sites | +| --- | --- | +| `DocumentUpdatedForSubject` | 1 (`_setDocument`) | +| `DocumentRemovedForSubject` | 1 (`_removeDocument`) | +| `TokenBindingSet` | 1 (`_setTokenBinding`) | + +All three already funnel through a single internal writer that owns validation + write + event, and +every public entrypoint delegates to it — including the batch variants, which call `_setDocument` / +`_removeDocument` rather than re-implementing. This is the shape the check exists to recommend; it is +already in place. + +**Verdict: nothing to do.** Recorded because it is the strongest structural property in the codebase +and a future refactor should preserve it. + +### C-2. The trusted forwarder is not evented at construction + +`DocumentEngine.sol:40` / `DocumentEngineOwnable.sol:32` pass `forwarderIrrevocable` to +`ERC2771Context` and emit nothing, so a log-only indexer never sees the value. That matters more than +usual here because the forwarder can act as any bound subject (`IMPROVEMENT.md` item 5). + +Against that: the value is `immutable`, so it can never change and there is no sequence to +reconstruct; it is publicly readable — `trustedForwarder()` (`0x7da0a877`) and +`isTrustedForwarder(address)` are both in the ABI, confirmed with `forge inspect`; and OpenZeppelin +itself emits nothing here, so adding an event departs from upstream for a one-off value anyone can +read. + +**Verdict: leave.** A single `SLOAD`-free public getter of an immutable is adequate observability. + +## D. Duplication + +### D-1. The ERC-2771 trio is byte-identical across both deployments — and cannot be shared + +`DocumentEngine.sol:137-157` and `DocumentEngineOwnable.sol:73-93` contain `_msgSender`, `_msgData` +and `_contextSuffixLength`, **10 code lines each** (NatSpec excluded), byte-for-byte identical +(`diff` confirms). + +Both projects this codebase cites as its pattern reference do extract this: RuleEngine has +`ERC2771ModuleStandalone`, CMTAT has `ERC2771Module` plus `6_CMTATBaseERC2771.sol`, which holds the +trio and marks it `virtual`. On that evidence the obvious recommendation is "extract a shared +`ERC2771Module`". + +**I built it, and it does not compile.** First attempt — module inherits `ERC2771Context` and +declares the three overrides: + +``` +Error (2353): Invalid contract specified in override list: "Context". +Error (6480): Derived contract must override function "_msgData". + Two or more base classes define function with same name and parameter types. + --> src/DocumentEngine.sol +``` + +Second attempt — module inherits `Context, ERC2771Context`, which fixes the first error: + +``` +Error (6480): Derived contract must override function "_msgSender". +Error (6480): Derived contract must override function "_msgData". +Error (6480): Derived contract must override function "_contextSuffixLength". + --> src/DocumentEngine.sol +``` + +The reason is C3 linearization, and it is why the reference projects can do what this one cannot. +CMTAT's inheritance is a single linear chain (`1_…` → `7_`), so one base can resolve `Context` for +everything below it. Here the two deployments **diverge at the access-control base** — +`AccessControlEnumerable` in one, `Ownable2Step` in the other — and each of those brings its own +`Context` branch. Solidity requires the most-derived contract to resolve the ambiguity, so the +override must be re-stated in each deployment no matter what a shared parent does. + +**Verdict: leave.** The duplication is forced by the language, not an oversight. Ten lines is the +price of supporting two access-control models, and this entry exists so the next reviewer does not +spend the same hour discovering it. (RuleEngine's `ERC2771ModuleStandalone`, worth noting, contains +only a constructor — *not* the trio — which is consistent with this conclusion.) + +## E. `virtual` / override convention + +### E-1. The two modules disagree about what is overridable + +`CLAUDE.md` states the convention as *"restricted functions use the `onlyDocumentManager` / +`onlyBoundToken` modifiers, which delegate to overridable `internal virtual` hooks"*. Both hooks are +`virtual`, so the documented convention is met. The inconsistency is one level out: + +| contract | `virtual` | not `virtual` | +| --- | --- | --- | +| `TokenBindingModule` | `bindToken`, `unbindToken`, `isTokenBound`, `_authorizeBoundTokenDocumentManagement` | `_setTokenBinding`, `_checkTokenBound` | +| `DocumentEngineBase` | `_authorizeDocumentManagement`, `_authorizeBoundTokenDocumentManagement` (both abstract) | all 13 others — `setDocument` ×2, `removeDocument` ×2, `batchSetDocuments` ×2, `batchRemoveDocuments` ×2, `getDocument` ×2, `getAllDocuments` ×2, `_setDocument`, `_removeDocument`, `_removeDocumentName`, `_getDocument` | + +So `TokenBindingModule` exposes its whole public surface for override while hiding its internals, and +`DocumentEngineBase` does the reverse — nothing overridable but the two abstract hooks. Two modules +in one codebase, opposite conventions. That inconsistency is the finding, independent of which is +right. + +For reference, CMTAT's `DocumentERC1643Module` — the module this engine mirrors — is **5 of 5** +public/external/internal functions `virtual`. + +The consequence is concrete: a deployment cannot override `getAllDocuments` to paginate, or +`setDocument` to add a per-subject policy, even though the architecture is explicitly built around +subclassing (the suite's own `OpenDocumentEngine` demonstrates the pattern). + +Cost: **zero**, measured rather than asserted — `virtual` on an internal function is resolved +statically unless actually overridden: + +| variant | gas | +| --- | --- | +| `internal` | 885 | +| `internal virtual` | 885 | + +**Verdict: decide.** Two coherent options, either better than today's split: +1. **Match CMTAT** — mark the `DocumentEngineBase` public API and the `TokenBindingModule` internals + `virtual`. Maximum extensibility, zero runtime cost, but it commits the project to a much larger + override surface as public API. +2. **Tighten instead** — drop `virtual` from `bindToken`/`unbindToken`/`isTokenBound` so that only the + authorization hooks are overridable, matching what `CLAUDE.md` actually promises. + +Not implemented: this is a commitment about the extension surface, which is the maintainer's call, +not a reviewer's. + +## F. ERC / specification conformance + +### F-1. No ERC-165 inherited-selector trap — verified correct + +The classic bug is `type(IFoo).interfaceId` covering only the selectors declared *directly* on `IFoo` +while the contract advertises it as covering inherited ones too. Checked all four interfaces: + +| interface | inherits | functions declared directly | id covers all | +| --- | --- | --- | --- | +| `IERC1643` (CMTAT) | nothing | 4 | yes | +| `IERC1643MultiDocument` | nothing (deliberately not `IERC1643`) | 4 | yes | +| `ITokenBinding` | nothing | 3 | yes | +| `IERC8303` | nothing | 1 | yes | + +Every interface is flat, so each `interfaceId` is the complete XOR of its surface and the trap cannot +arise. `IERC1643MultiDocument`'s deliberate non-inheritance of `IERC1643` — documented in its own +NatSpec — is what makes this safe, and is worth preserving for that reason as well as the one already +given. + +Sentinel handling checked too: `_setDocument` rejects `subject == address(0)` +(`MultiDocumentInvalidSubject`) and `_setTokenBinding` rejects `token == address(0)` +(`TokenBindingInvalidToken`), so `address(0)` can never become a document-holding subject. The read +paths do not re-check it, but a read against a namespace that cannot be populated returns empty and +is harmless. + +**Verdict: nothing to do.** + +## G. Code / documentation mismatch + +### G-1. `DocumentEngineInvariant` misattributes an error — **fixed** + +`DocumentEngineInvariant.sol` carried a comment mapping each specification error to the interface +that declares it: + +``` +// - `NotBoundToken(address)` → `ITokenBinding` +``` + +`NotBoundToken` is **not** declared by `ITokenBinding`. It is declared in `TokenBindingModule` +(`error NotBoundToken(address caller);`); `ITokenBinding` declares only `TokenBindingInvalidToken`. +The comment exists precisely to tell a reader where each error lives, so an incorrect entry defeats +its own purpose — and this one would send an integrator building an ABI from `ITokenBinding` looking +for a selector that is not there. + +**Verdict: implemented** — the line now names `TokenBindingModule`, with a note on why that one +differs (it is the module's own operational error, not a specification error, so no interface +declares it). + +### G-2. Three contract comments point at documentation paths — one has already broken once + +``` +src/DocumentEngineBase.sol:289 // responsibility). See doc/ERCSpecification. +src/DocumentEngineBase.sol:325 // event (see {_removeDocument} note and doc/ERCSpecification). +src/interfaces/IERC1643MultiDocument.sol:13 + * on-chain product). See `doc/ERCSpecification/erc-draft_multi_document_management.md`. +``` + +Documentation moves; deployed source does not. Someone reading verified source on a block explorer +has the comment and not the file. This is normally a theoretical risk — here it is a demonstrated +one, from this repo's own history: + +``` +1233b42 A doc/ERCSpecification/ERC-1643-proposition.md +113a348 D doc/ERCSpecification/ERC-1643-proposition.md +5d13ee0 A doc/ERCSpecification/erc-draft_multi_document_management.md +``` + +The file was added, deleted, and replaced under a different name inside four commits. A `README` +pointer to the old name survived that rename as a dangling link until it was fixed in this session. +The pointer now baked into `IERC1643MultiDocument.sol` names the *replacement*, which is one rename +away from the same fate — except that this one would be frozen in verified bytecode. + +**Verdict: decide.** The fix is not deletion — it is to move the substance in and drop only the +pointer. The three sites differ: +- `DocumentEngineBase.sol:289` — the preceding sentence already states the emission rule in full; the + pointer comes out cleanly. +- `DocumentEngineBase.sol:325` — same, and the `{_removeDocument}` cross-reference is a NatSpec link + that resolves within the source, so it stays. +- `IERC1643MultiDocument.sol:13` — this one *leans* on the document; removing the pointer alone + leaves "the reasoning applies to any … on-chain product" with no statement of what the reasoning + is. A replacement clause has to be written. + +Not implemented because that third site needs an editorial decision about how much of the draft's +rationale belongs in the interface. + +Two exemptions deliberately **not** flagged: mocks and tests (never deployed), and citations of audit +records by bare filename (`CLAUDE_ANALYSIS.md` plus a finding ID) — those are immutable historical +records, the bare filename survives a move, and the ID carries context a comment cannot restate. The +regression test added under H-1 cites this report exactly that way, on purpose. + +### G-3. NatSpec block lengths are healthy — measured, nothing to do + +The usual finding here is a handful of 30–40 line contract headers a reader must wade through before +reaching any code. Measured across `src/`: + +| metric | value | +| --- | --- | +| NatSpec blocks | 65 | +| median | 6 lines | +| 90th percentile | 11 lines | +| max | 24 lines | +| blocks ≥ 20 lines | **1** | + +One outlier: the 24-line block on `DocumentEngine.supportsInterface`. Its content is a genuine +footgun warning — that advertising `type(IERC1643).interfaceId` is *not* an invitation to read +documents from the engine's address, because the base functions are caller-scoped and a third-party +read silently returns an empty namespace. That is a safety precondition with a non-obvious failure +mode, which is exactly what earns space in a comment. + +**Verdict: nothing to do.** Reported with the distribution attached, because "24 lines" only means +something next to a median of 6 — and here the ratio is defensible. + +## H. Weird behaviour + +### H-1. Revoking a role from the default admin succeeds but removes nothing — **documented** + +`DocumentEngine.sol:73` overrides `hasRole` so that `DEFAULT_ADMIN_ROLE` implicitly holds every role. +The existing NatSpec documented one consequence (the enumeration mismatch). It did not document this +one, which I verified by running it: + +``` +after revokeRole, hasRole(admin): TRUE +getRoleMemberCount: 0 +admin STILL wrote a document after its role was revoked +``` + +`revokeRole(DOCUMENT_MANAGER_ROLE, admin)` **succeeds**, emits `RoleRevoked`, and genuinely removes +the explicit grant — `getRoleMemberCount` drops to 0. Yet `hasRole` still answers `true`, so the +admin sails through `_checkRole` and writes a document. An operator watching events, or a dashboard +reading `getRoleMemberCount`, sees a successful revocation that did not happen. The contrast case +behaves correctly: an ordinary grantee is properly blocked after revocation. + +**Why this is a quality finding and not a vulnerability.** No privilege is gained. The default admin +is the most privileged account by construction and can call `grantRole` to restore any role in the +same transaction, so "revoking a role from the admin" could never have been a durable restriction — +only revoking `DEFAULT_ADMIN_ROLE` itself withdraws anything. The defect is that the call reports +success for something it cannot do. + +**Verdict: documented, not changed.** Making `revokeRole` revert here would deviate from +`IAccessControl` semantics and break the "admin has all roles" model the contract deliberately +adopts. Instead: +- a `WARNING:` paragraph was added to the `hasRole` NatSpec stating that a role is unrevokable from + the default admin and that only `DEFAULT_ADMIN_ROLE` itself can be withdrawn; +- `testRevokingRoleFromDefaultAdminDoesNotRemoveAccess` pins the behaviour, asserting all three + facts — `hasRole` still true, `getRoleMemberCount` zero, write still succeeds — so the surprise is + a tested property rather than a latent one. + +### H-2. Caller-scoped reads return empty rather than reverting + +`getDocument(bytes32)` and `getAllDocuments()` resolve against `_msgSender()`. A third party calling +them on the engine reads *its own* namespace: no revert, no error, just empty values. This is the +"hardcoded everything-is-fine answer" shape, and it travels — an integrator who wires a UI to the +engine address sees a document set that is silently empty rather than an error telling them they +asked the wrong contract. + +**Verdict: leave.** This is inherent to ERC-1643's single-argument signature, which has no subject +parameter; the engine cannot know which namespace a reader meant. It is already handled about as well +as it can be: stated in the README, in both `supportsInterface` NatSpec blocks, and asserted by +`testBaseERC1643IsAdvertisedButReadsAreCallerScoped` and +`testMsgSenderScopedReadReturnsEmptyForOther`. The address-scoped `getDocument(subject, name)` is the +correct entrypoint for third parties and is advertised through `IERC1643MultiDocument`. + +Recorded here so it is visible as a deliberate trade-off rather than rediscovered as a defect. + +--- + +## Verification performed + +- `forge build` — clean; `forge test` — **73/73 passing** (72 before, +1 regression test from H-1). +- `forge fmt --check` — clean. Style checker (`check_order.py`) — 0 violations across `src/` + `script/`. +- Storage layout re-read from compiled artifacts after B-1/B-2 + (`forge build --force --extra-output storageLayout`): 5 non-empty entries per deployment, unchanged. + No signature, visibility or ABI change in any finding implemented. +- All four temporary benchmark harnesses deleted; test count returned to its expected value. +- Gas figures come from `gasleft()` deltas after identical warm-ups, each variant either in its own + single-function contract (A-1, E-1) or toggled in place and re-run against the same harness + (A-2, B-1, B-2). + +## What was assumed rather than executed + +- The claim in A-1 that solc elides the bounded-counter overflow check *from 0.8.22 onwards* is the + documented compiler behaviour; what I measured is that on **0.8.34** the delta is zero. I did not + bisect the compiler versions. +- D-1's conclusion is that a shared module cannot resolve the override under C3 linearization. I + proved the two natural formulations fail to compile; I did not exhaustively enumerate every + possible inheritance arrangement. +- H-2's reach ("an integrator who wires a UI to the engine address") is reasoning about consumer + behaviour, not something observed. diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index 79b6c48..29eece7 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -56,6 +56,15 @@ contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnume * {AccessControlEnumerable} enumeration. `getRoleMember` / `getRoleMemberCount` * report only explicit grants, so a `DEFAULT_ADMIN_ROLE` holder satisfies * `hasRole(anyRole, admin)` yet does not appear in `getRoleMember(anyRole, ...)`. + * + * WARNING: the same short-circuit makes a role **unrevokable from the default admin**. + * `revokeRole(someRole, admin)` succeeds and emits `RoleRevoked` — the explicit grant is + * genuinely removed, and `getRoleMemberCount` drops — but this function still answers + * `true`, so the admin keeps the access the caller believed it had just removed. Only + * revoking `DEFAULT_ADMIN_ROLE` itself actually withdraws it. This is inherent to the + * "admin has all roles" model rather than a defect (an admin can always re-grant itself + * any role), but the success of the call is misleading. Pinned by + * `testRevokingRoleFromDefaultAdminDoesNotRemoveAccess`. * @param role The role identifier to check. * @param account The account to check. * @return True when `account` holds `role`, or holds `DEFAULT_ADMIN_ROLE`. diff --git a/src/DocumentEngineBase.sol b/src/DocumentEngineBase.sol index 1eb97cc..ba57523 100644 --- a/src/DocumentEngineBase.sol +++ b/src/DocumentEngineBase.sol @@ -260,11 +260,12 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen * @param name_ The document name to remove from the list. */ function _removeDocumentName(address subject, bytes32 name_) internal { - uint256 length = _documentNames[subject].length; + bytes32[] storage names = _documentNames[subject]; + uint256 length = names.length; for (uint256 i = 0; i < length; ++i) { - if (_documentNames[subject][i] == name_) { - _documentNames[subject][i] = _documentNames[subject][length - 1]; - _documentNames[subject].pop(); + if (names[i] == name_) { + names[i] = names[length - 1]; + names.pop(); break; } } @@ -277,7 +278,7 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen * @param name_ The document name. */ function _removeDocument(address subject, bytes32 name_) internal { - Document memory doc = _documents[subject][name_]; + Document storage doc = _documents[subject][name_]; // ERC-1643: reverts when the named document does not exist if (doc.lastModified == 0) { revert ERC1643MissingDocument(); diff --git a/src/DocumentEngineInvariant.sol b/src/DocumentEngineInvariant.sol index 250ee9d..d89ae93 100644 --- a/src/DocumentEngineInvariant.sol +++ b/src/DocumentEngineInvariant.sol @@ -20,6 +20,10 @@ abstract contract DocumentEngineInvariant { // multi-subject draft's "MUST NOT declare them twice", which the compiler also enforces: // - `ERC1643InvalidName()` / `ERC1643MissingDocument()` → `IERC1643` (since CMTAT v3.3.0-rc2) // - `MultiDocumentInvalidSubject()` → `IERC1643MultiDocument` - // - `NotBoundToken(address)` → `ITokenBinding` // - `TokenBindingInvalidToken()` → `ITokenBinding` + // + // `NotBoundToken(address)` is the one exception, declared by `TokenBindingModule` rather than by + // an interface: it reports that the *caller* is not on the module's allowlist, which is an + // implementation detail of how binding is enforced, not a condition any interface specifies. + // `ITokenBinding` deliberately declares only `TokenBindingInvalidToken()`. } diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index 96b7b94..a5c09b0 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -116,6 +116,28 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentEngine.setDocument(testContract, documentName, documentURI, documentHash); } + /** + * @dev Pins the consequence of the "default admin holds every role" {hasRole} override: + * `revokeRole` against the default admin SUCCEEDS and emits `RoleRevoked`, yet the admin + * keeps the access. The revocation is not silently ignored by mistake — the explicit grant + * really is removed (`getRoleMemberCount` drops) — but `hasRole` still answers `true` + * because the override short-circuits on `DEFAULT_ADMIN_ROLE`, so the authorization gate + * still lets the admin through. Recorded in CLAUDE_ANALYSIS.md (H-1). + */ + function testRevokingRoleFromDefaultAdminDoesNotRemoveAccess() public { + assertTrue(documentEngine.hasRole(DOCUMENT_MANAGER_ROLE, admin), "admin implicitly holds the role"); + + vm.prank(admin); + documentEngine.revokeRole(DOCUMENT_MANAGER_ROLE, admin); // succeeds, no revert + + assertTrue(documentEngine.hasRole(DOCUMENT_MANAGER_ROLE, admin), "revoke does NOT take the role from the admin"); + assertEq(documentEngine.getRoleMemberCount(DOCUMENT_MANAGER_ROLE), 0, "no explicit grant remains"); + + // and the admin still passes the authorization gate + vm.prank(admin); + documentEngine.setDocument(testContract, documentName, documentURI, documentHash); + } + function testCannotNonAdminRemoveDocument() public { vm.prank(attacker); vm.expectRevert( From 0394e30f841cc834e95f7c7b31e93f10a98416bb Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:42:30 +0200 Subject: [PATCH 32/47] refactor: mark every internal function virtual (zero-gas, guarded by an overriding test harness) --- AGENTS.md | 6 +++ CHANGELOG.md | 10 +++++ CLAUDE.md | 6 +++ doc/audits/CLAUDE_ANALYSIS.md | 53 ++++++++++++++++++++------ src/DocumentEngine.sol | 6 +-- src/DocumentEngineBase.sol | 7 ++-- src/DocumentEngineOwnable.sol | 6 +-- src/modules/TokenBindingModule.sol | 4 +- test/DocumentEngine.t.sol | 60 ++++++++++++++++++++++++++++++ 9 files changed, 136 insertions(+), 22 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 720b3be..ef8f725 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -70,6 +70,12 @@ addressed by a `bytes32` name. - **ERC-2771:** meta-transaction (gasless) support; `_msgSender()` is used everywhere. - **Access control:** `DEFAULT_ADMIN_ROLE` implicitly has every role (see the `hasRole` override). +- **Every `internal` function is `virtual`.** Not just the `_authorize*` hooks — the document + write/read paths (`_setDocument`, `_removeDocument`, `_removeDocumentName`, `_getDocument`), the + binding internals (`_setTokenBinding`, `_checkTokenBound`) and the ERC-2771 context trio are all + overridable. It costs nothing at runtime (bytecode is byte-identical with and without the keyword), + so **keep new internal functions `virtual`**; `OverridingDocumentEngine` in the test suite fails to + compile if one loses it. - **Flexible access control (CMTAT / RuleEngine pattern):** restricted functions use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to overridable `internal virtual` hooks `_authorizeDocumentManagement()` (per diff --git a/CHANGELOG.md b/CHANGELOG.md index e6d0130..85bf439 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,6 +87,16 @@ for which CMTAT release each version of this engine is built against. Pinned by the new `testRevokingRoleFromDefaultAdminDoesNotRemoveAccess`. - **`DocumentEngineInvariant`**: the error-location comment misattributed `NotBoundToken(address)` to `ITokenBinding`; it is declared by `TokenBindingModule`. + - **All 12 `internal` functions are now `virtual`** (`_setDocument`, `_removeDocument`, + `_removeDocumentName`, `_getDocument`, `_setTokenBinding`, `_checkTokenBound`, and the ERC-2771 + context trio in both deployments), resolving an inconsistency where `TokenBindingModule` exposed + its public surface for override while `DocumentEngineBase` exposed nothing but its two abstract + hooks. A deployment can now override the document write/read paths and the binding check, matching + what CMTAT's equivalent module allows. **Runtime cost is zero:** the executable bytecode of both + deployments is byte-identical before and after (7457 / 6111 bytes, metadata trailer excluded). + Guarded by `OverridingDocumentEngine` + + `testInternalHooksAreVirtualAndOverridesAreReached` — removing `virtual` from any of the three + overridden hooks fails the build (`Error (4334): Trying to override non-virtual function`). Notable non-changes, recorded so they are not re-raised: `unchecked { ++i }` buys **0 gas** on solc 0.8.34 (measured); `string calldata` on the admin `setDocument` is **49 gas worse** than `memory` diff --git a/CLAUDE.md b/CLAUDE.md index 720b3be..ef8f725 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -70,6 +70,12 @@ addressed by a `bytes32` name. - **ERC-2771:** meta-transaction (gasless) support; `_msgSender()` is used everywhere. - **Access control:** `DEFAULT_ADMIN_ROLE` implicitly has every role (see the `hasRole` override). +- **Every `internal` function is `virtual`.** Not just the `_authorize*` hooks — the document + write/read paths (`_setDocument`, `_removeDocument`, `_removeDocumentName`, `_getDocument`), the + binding internals (`_setTokenBinding`, `_checkTokenBound`) and the ERC-2771 context trio are all + overridable. It costs nothing at runtime (bytecode is byte-identical with and without the keyword), + so **keep new internal functions `virtual`**; `OverridingDocumentEngine` in the test suite fails to + compile if one loses it. - **Flexible access control (CMTAT / RuleEngine pattern):** restricted functions use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to overridable `internal virtual` hooks `_authorizeDocumentManagement()` (per diff --git a/doc/audits/CLAUDE_ANALYSIS.md b/doc/audits/CLAUDE_ANALYSIS.md index 1a9bbdf..463629d 100644 --- a/doc/audits/CLAUDE_ANALYSIS.md +++ b/doc/audits/CLAUDE_ANALYSIS.md @@ -31,7 +31,7 @@ | C-1 | Every event has exactly one emit site | ⬜ nothing to do — verified good | — | | C-2 | Trusted forwarder set at construction without an event | ⬜ left as is | `DocumentEngine.sol:40` | | D-1 | ERC-2771 trio duplicated byte-for-byte across both deployments | ⬜ left as is — **extraction proven impossible** | `DocumentEngine.sol:137`, `DocumentEngineOwnable.sol:73` | -| E-1 | `virtual` coverage inconsistent between the two modules | ⚠️ decide — not implemented | `DocumentEngineBase.sol`, `TokenBindingModule.sol` | +| E-1 | `virtual` coverage inconsistent between the two modules | ✅ fixed — all 12 internal functions now `virtual` | `DocumentEngineBase.sol`, `TokenBindingModule.sol`, both deployments | | F-1 | ERC-165 interface IDs — no inherited-selector trap | ⬜ nothing to do — verified correct | `DocumentEngine.sol:115` | | G-1 | `DocumentEngineInvariant` comment misattributes `NotBoundToken` | ✅ fixed | `DocumentEngineInvariant.sol` | | G-2 | Contracts point at documentation paths that have already moved once | ⚠️ decide — not implemented | 3 sites | @@ -39,13 +39,12 @@ | H-1 | A role cannot be revoked from the default admin, but the call succeeds | ✅ documented + regression test | `DocumentEngine.sol:73` | | H-2 | Caller-scoped reads return an empty namespace instead of reverting | ⬜ left as is — already documented and tested | `DocumentEngineBase.sol:186` | -Rows: 14. Fixed: 4. Left deliberately: 8. Open decisions: 2. +Rows: 14. Fixed: 5. Left deliberately: 8. Open decisions: 1. ## Outstanding | ID | Item | Why it is still open | | --- | --- | --- | -| E-1 | Make the document-management API and the binding internals `virtual` | Changes the extension surface the project commits to. Free at runtime (measured, 0 gas) but it is a design commitment, so it is the maintainer's call — see the two options in E-1. | | G-2 | Remove the `doc/…` pointers baked into contract comments | Two of the three sites *lean* on the doc rather than merely citing it; removing the pointer alone would leave an incomplete warning. Needs a sentence written per site, which is an editorial decision. | --- @@ -283,15 +282,47 @@ statically unless actually overridden: | `internal` | 885 | | `internal virtual` | 885 | -**Verdict: decide.** Two coherent options, either better than today's split: -1. **Match CMTAT** — mark the `DocumentEngineBase` public API and the `TokenBindingModule` internals - `virtual`. Maximum extensibility, zero runtime cost, but it commits the project to a much larger - override surface as public API. -2. **Tighten instead** — drop `virtual` from `bindToken`/`unbindToken`/`isTokenBound` so that only the - authorization hooks are overridable, matching what `CLAUDE.md` actually promises. +**Verdict: implemented — all internal functions are now `virtual`.** The maintainer chose to widen +the internal surface rather than narrow the public one, which resolves the inconsistency in the +direction CMTAT takes while leaving the external API commitment unchanged. Twelve functions gained +the keyword: -Not implemented: this is a commitment about the extension surface, which is the maintainer's call, -not a reviewer's. +| contract | now `virtual` | +| --- | --- | +| `DocumentEngineBase` | `_removeDocumentName`, `_removeDocument`, `_setDocument`, `_getDocument` | +| `TokenBindingModule` | `_setTokenBinding`, `_checkTokenBound` | +| `DocumentEngine` | `_msgSender`, `_msgData`, `_contextSuffixLength` | +| `DocumentEngineOwnable` | `_msgSender`, `_msgData`, `_contextSuffixLength` | + +`virtual` sits in the style-guide keyword position (visibility → mutability → `virtual` → `override`), +so the style checker still reports 0 `[modifier-order]` violations. + +**Runtime cost: zero, and this time proven on the real contracts rather than a synthetic pair.** +Runtime bytecode before and after, with the CBOR metadata trailer stripped: + +| contract | executable code before | after | | +| --- | --- | --- | --- | +| `DocumentEngine` | 7457 bytes | 7457 bytes | **byte-identical** | +| `DocumentEngineOwnable` | 6111 bytes | 6111 bytes | **byte-identical** | + +Only the metadata hash moved, because the source text changed. Solidity resolves an unoverridden +`virtual` internal call statically, so nothing reaches the runtime — consistent with the earlier +885-vs-885 synthetic measurement, now confirmed against production code. + +**The guard.** A convention-only change needs a harness that *compiles*, so +`OverridingDocumentEngine` (in `DocumentEngine.t.sol`) overrides three of the twelve — one from the +write path (`_setDocument`), one from the read path (`_getDocument`), one from the authorization path +(`_checkTokenBound`). Removing `virtual` from any of them breaks the build, verified by doing it: + +``` +Error (4334): Trying to override non-virtual function. Did you forget to add "virtual"? + --> src/modules/TokenBindingModule.sol:88:5 +``` + +A compile-only check would not catch a silently shadowed override, so +`testInternalHooksAreVirtualAndOverridesAreReached` additionally asserts each override is on the real +call path: the counter increments, the read comes back URI-tagged, and an **unbound** caller passes +the bound-token gate that would otherwise revert `NotBoundToken`. ## F. ERC / specification conformance diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol index 29eece7..6c51be4 100644 --- a/src/DocumentEngine.sol +++ b/src/DocumentEngine.sol @@ -143,7 +143,7 @@ contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnume * @return sender The transaction sender, unwrapped from the ERC-2771 calldata suffix when the * call came through the trusted forwarder. */ - function _msgSender() internal view override(ERC2771Context, Context) returns (address sender) { + function _msgSender() internal view virtual override(ERC2771Context, Context) returns (address sender) { return ERC2771Context._msgSender(); } @@ -152,7 +152,7 @@ contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnume * @return The calldata, stripped of the ERC-2771 sender suffix when the call came through the * trusted forwarder. */ - function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata) { + function _msgData() internal view virtual override(ERC2771Context, Context) returns (bytes calldata) { return ERC2771Context._msgData(); } @@ -160,7 +160,7 @@ contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnume * @dev This surcharge is not necessary if you do not use the MetaTxModule * @return The length of the ERC-2771 calldata suffix holding the sender address. */ - function _contextSuffixLength() internal view override(ERC2771Context, Context) returns (uint256) { + function _contextSuffixLength() internal view virtual override(ERC2771Context, Context) returns (uint256) { return ERC2771Context._contextSuffixLength(); } } diff --git a/src/DocumentEngineBase.sol b/src/DocumentEngineBase.sol index ba57523..93299c3 100644 --- a/src/DocumentEngineBase.sol +++ b/src/DocumentEngineBase.sol @@ -259,7 +259,7 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen * @param subject The contract the document belongs to. * @param name_ The document name to remove from the list. */ - function _removeDocumentName(address subject, bytes32 name_) internal { + function _removeDocumentName(address subject, bytes32 name_) internal virtual { bytes32[] storage names = _documentNames[subject]; uint256 length = names.length; for (uint256 i = 0; i < length; ++i) { @@ -277,7 +277,7 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen * @param subject The contract the document belongs to. * @param name_ The document name. */ - function _removeDocument(address subject, bytes32 name_) internal { + function _removeDocument(address subject, bytes32 name_) internal virtual { Document storage doc = _documents[subject][name_]; // ERC-1643: reverts when the named document does not exist if (doc.lastModified == 0) { @@ -302,7 +302,7 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen * @param uri_ The document location. * @param documentHash_ The hash of the document contents. */ - function _setDocument(address subject, bytes32 name_, string memory uri_, bytes32 documentHash_) internal { + function _setDocument(address subject, bytes32 name_, string memory uri_, bytes32 documentHash_) internal virtual { // Multi-token guard: `subject` must be a real contract address, never the // null namespace. (The bound-token path passes `_msgSender()`, never zero.) if (subject == address(0)) { @@ -354,6 +354,7 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen function _getDocument(address subject, bytes32 name_) internal view + virtual returns (string memory uri, bytes32 documentHash, uint256 lastModified) { Document storage doc = _documents[subject][name_]; diff --git a/src/DocumentEngineOwnable.sol b/src/DocumentEngineOwnable.sol index 30666cd..8a9fa72 100644 --- a/src/DocumentEngineOwnable.sol +++ b/src/DocumentEngineOwnable.sol @@ -70,7 +70,7 @@ contract DocumentEngineOwnable is TokenBindingModule, VersionModule, Ownable2Ste * @return sender The transaction sender, unwrapped from the ERC-2771 calldata suffix when the * call came through the trusted forwarder. */ - function _msgSender() internal view override(ERC2771Context, Context) returns (address sender) { + function _msgSender() internal view virtual override(ERC2771Context, Context) returns (address sender) { return ERC2771Context._msgSender(); } @@ -79,7 +79,7 @@ contract DocumentEngineOwnable is TokenBindingModule, VersionModule, Ownable2Ste * @return The calldata, stripped of the ERC-2771 sender suffix when the call came through the * trusted forwarder. */ - function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata) { + function _msgData() internal view virtual override(ERC2771Context, Context) returns (bytes calldata) { return ERC2771Context._msgData(); } @@ -87,7 +87,7 @@ contract DocumentEngineOwnable is TokenBindingModule, VersionModule, Ownable2Ste * @dev This surcharge is not necessary if you do not use the MetaTxModule * @return The length of the ERC-2771 calldata suffix holding the sender address. */ - function _contextSuffixLength() internal view override(ERC2771Context, Context) returns (uint256) { + function _contextSuffixLength() internal view virtual override(ERC2771Context, Context) returns (uint256) { return ERC2771Context._contextSuffixLength(); } } diff --git a/src/modules/TokenBindingModule.sol b/src/modules/TokenBindingModule.sol index bb5e56f..513b088 100644 --- a/src/modules/TokenBindingModule.sol +++ b/src/modules/TokenBindingModule.sol @@ -65,7 +65,7 @@ abstract contract TokenBindingModule is DocumentEngineBase, ITokenBinding { * @param token The token whose binding is being set. * @param bound The binding state to apply: `true` to bind, `false` to unbind. */ - function _setTokenBinding(address token, bool bound) internal { + function _setTokenBinding(address token, bool bound) internal virtual { if (token == address(0)) { revert TokenBindingInvalidToken(); } @@ -85,7 +85,7 @@ abstract contract TokenBindingModule is DocumentEngineBase, ITokenBinding { } /// @dev Reverts {NotBoundToken} if the caller (`_msgSender()`) is not bound. - function _checkTokenBound() internal view { + function _checkTokenBound() internal view virtual { if (!_boundTokens[_msgSender()]) { revert NotBoundToken(_msgSender()); } diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol index a5c09b0..845496f 100644 --- a/test/DocumentEngine.t.sol +++ b/test/DocumentEngine.t.sol @@ -41,6 +41,41 @@ contract OpenDocumentEngine is DocumentEngine { } } +/** + * @dev Guard for the `internal virtual` convention (CLAUDE_ANALYSIS.md E-1). + * + * Overrides three of the internal hooks — one from `DocumentEngineBase`'s write path, one from its + * read path, and one from `TokenBindingModule`'s authorization path. Two things are being pinned: + * dropping `virtual` from any of them stops this contract compiling, and the assertions in + * {DocumentEngineTest-testInternalHooksAreVirtualAndOverridesAreReached} prove each override is + * actually reached rather than silently shadowed. + */ +contract OverridingDocumentEngine is DocumentEngine { + uint256 public setDocumentCalls; + + constructor(address admin_, address forwarder) DocumentEngine(admin_, forwarder) {} + + /// @dev Counts invocations, then defers to the base implementation. + function _setDocument(address subject, bytes32 name_, string memory uri_, bytes32 documentHash_) internal override { + ++setDocumentCalls; + super._setDocument(subject, name_, uri_, documentHash_); + } + + /// @dev Tags the URI so a caller can observe that this override ran. + function _getDocument(address subject, bytes32 name_) + internal + view + override + returns (string memory uri, bytes32 documentHash, uint256 lastModified) + { + (uri, documentHash, lastModified) = super._getDocument(subject, name_); + uri = string.concat("override:", uri); + } + + /// @dev Deliberately permissive: every caller passes the bound-token gate. + function _checkTokenBound() internal view override {} +} + contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { DocumentEngine public documentEngine; address public admin = address(0x1); @@ -138,6 +173,31 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl { documentEngine.setDocument(testContract, documentName, documentURI, documentHash); } + /** + * @dev See {OverridingDocumentEngine}. Compilation alone proves the three hooks are `virtual`; + * these assertions prove each override is on the real call path. + */ + function testInternalHooksAreVirtualAndOverridesAreReached() public { + OverridingDocumentEngine engine = new OverridingDocumentEngine(admin, AddressZero); + + // _setDocument override reached on the admin write path + vm.prank(admin); + engine.setDocument(testContract, documentName, documentURI, documentHash); + assertEq(engine.setDocumentCalls(), 1, "_setDocument override not reached"); + + // _getDocument override reached on the read path + (string memory uri,,) = engine.getDocument(testContract, documentName); + assertEq(uri, string.concat("override:", documentURI), "_getDocument override not reached"); + + // _checkTokenBound override reached: an UNBOUND caller now passes the bound-token gate, + // which would otherwise revert NotBoundToken. + vm.prank(attacker); + engine.setDocument(documentName, documentURI, documentHash); + assertEq(engine.setDocumentCalls(), 2, "bound-token path did not run"); + (string memory ownUri,,) = engine.getDocument(attacker, documentName); + assertEq(ownUri, string.concat("override:", documentURI), "_checkTokenBound override not reached"); + } + function testCannotNonAdminRemoveDocument() public { vm.prank(attacker); vm.expectRevert( From 3f600da26e4abe686d3134f85c0b53779e6b2621 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:46:04 +0200 Subject: [PATCH 33/47] docs: drop doc/ pointers from contract comments, tightening each in place --- AGENTS.md | 6 ++++ CHANGELOG.md | 10 +++++- CLAUDE.md | 6 ++++ doc/audits/CLAUDE_ANALYSIS.md | 43 ++++++++++++++---------- src/DocumentEngineBase.sol | 4 +-- src/interfaces/IERC1643MultiDocument.sol | 4 +-- 6 files changed, 50 insertions(+), 23 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ef8f725..5f0ae71 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -70,6 +70,12 @@ addressed by a `bytes32` name. - **ERC-2771:** meta-transaction (gasless) support; `_msgSender()` is used everywhere. - **Access control:** `DEFAULT_ADMIN_ROLE` implicitly has every role (see the `hasRole` override). +- **No documentation pointers in contract comments.** Never write `See doc/…` or a `.md` path in + `src/` — docs move, deployed source does not, and a reader on a block explorer has neither. State + the conclusion in the comment instead, and keep it short; the derivation belongs in `doc/` with no + cross-reference either way. NatSpec links that resolve inside the source (`{_removeDocument}`) are + fine. Exempt: tests/mocks, and citations of audit records by **bare filename + finding ID** + (`CLAUDE_ANALYSIS.md (H-1)`) — those are immutable and survive a move. - **Every `internal` function is `virtual`.** Not just the `_authorize*` hooks — the document write/read paths (`_setDocument`, `_removeDocument`, `_removeDocumentName`, `_getDocument`), the binding internals (`_setTokenBinding`, `_checkTokenBound`) and the ERC-2771 context trio are all diff --git a/CHANGELOG.md b/CHANGELOG.md index 85bf439..273ead6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,7 +74,7 @@ for which CMTAT release each version of this engine is built against. - `foundry.lock` now records every submodule by tag; all five entries had gone stale since `v0.3.0`. - **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) - **Code-quality review** (`doc/audits/CLAUDE_ANALYSIS.md`) — 14 findings, none a vulnerability. - Four implemented: + Six implemented: - **Gas, `_removeDocumentName`**: the `_documentNames[subject]` mapping slot was re-hashed on every loop iteration; cached as a storage pointer. Measured **−2200 gas** on a 20-entry full scan. - **Gas, `_removeDocument`**: the whole `Document` (URI included) was copied to memory to be read @@ -87,6 +87,14 @@ for which CMTAT release each version of this engine is built against. Pinned by the new `testRevokingRoleFromDefaultAdminDoesNotRemoveAccess`. - **`DocumentEngineInvariant`**: the error-location comment misattributed `NotBoundToken(address)` to `ITokenBinding`; it is declared by `TokenBindingModule`. + - **Documentation pointers removed from contract comments.** Three comments referenced + `doc/ERCSpecification…`; documentation moves but deployed source does not, and this repo had + already renamed that file once (`ERC-1643-proposition.md` → `erc-draft_multi_document_management.md`), + leaving a dangling README link behind. Someone reading verified source on an explorer has the + comment and not the file. All three pointers are gone and each comment is now **shorter**, not + longer — the `IERC1643MultiDocument` header dropped from 10 lines to 9 by replacing an + enumeration that gestured at the draft's rationale with the one operative fact: `subject` need + not be a token. - **All 12 `internal` functions are now `virtual`** (`_setDocument`, `_removeDocument`, `_removeDocumentName`, `_getDocument`, `_setTokenBinding`, `_checkTokenBound`, and the ERC-2771 context trio in both deployments), resolving an inconsistency where `TokenBindingModule` exposed diff --git a/CLAUDE.md b/CLAUDE.md index ef8f725..5f0ae71 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -70,6 +70,12 @@ addressed by a `bytes32` name. - **ERC-2771:** meta-transaction (gasless) support; `_msgSender()` is used everywhere. - **Access control:** `DEFAULT_ADMIN_ROLE` implicitly has every role (see the `hasRole` override). +- **No documentation pointers in contract comments.** Never write `See doc/…` or a `.md` path in + `src/` — docs move, deployed source does not, and a reader on a block explorer has neither. State + the conclusion in the comment instead, and keep it short; the derivation belongs in `doc/` with no + cross-reference either way. NatSpec links that resolve inside the source (`{_removeDocument}`) are + fine. Exempt: tests/mocks, and citations of audit records by **bare filename + finding ID** + (`CLAUDE_ANALYSIS.md (H-1)`) — those are immutable and survive a move. - **Every `internal` function is `virtual`.** Not just the `_authorize*` hooks — the document write/read paths (`_setDocument`, `_removeDocument`, `_removeDocumentName`, `_getDocument`), the binding internals (`_setTokenBinding`, `_checkTokenBound`) and the ERC-2771 context trio are all diff --git a/doc/audits/CLAUDE_ANALYSIS.md b/doc/audits/CLAUDE_ANALYSIS.md index 463629d..10c2b62 100644 --- a/doc/audits/CLAUDE_ANALYSIS.md +++ b/doc/audits/CLAUDE_ANALYSIS.md @@ -34,18 +34,16 @@ | E-1 | `virtual` coverage inconsistent between the two modules | ✅ fixed — all 12 internal functions now `virtual` | `DocumentEngineBase.sol`, `TokenBindingModule.sol`, both deployments | | F-1 | ERC-165 interface IDs — no inherited-selector trap | ⬜ nothing to do — verified correct | `DocumentEngine.sol:115` | | G-1 | `DocumentEngineInvariant` comment misattributes `NotBoundToken` | ✅ fixed | `DocumentEngineInvariant.sol` | -| G-2 | Contracts point at documentation paths that have already moved once | ⚠️ decide — not implemented | 3 sites | +| G-2 | Contracts point at documentation paths that have already moved once | ✅ fixed — all 3 pointers removed, comments got *shorter* | `DocumentEngineBase.sol`, `IERC1643MultiDocument.sol` | | G-3 | NatSpec block-length distribution is healthy | ⬜ nothing to do — measured | — | | H-1 | A role cannot be revoked from the default admin, but the call succeeds | ✅ documented + regression test | `DocumentEngine.sol:73` | | H-2 | Caller-scoped reads return an empty namespace instead of reverting | ⬜ left as is — already documented and tested | `DocumentEngineBase.sol:186` | -Rows: 14. Fixed: 5. Left deliberately: 8. Open decisions: 1. +Rows: 14. Fixed: 6. Left deliberately: 8. Open decisions: none. ## Outstanding -| ID | Item | Why it is still open | -| --- | --- | --- | -| G-2 | Remove the `doc/…` pointers baked into contract comments | Two of the three sites *lean* on the doc rather than merely citing it; removing the pointer alone would leave an incomplete warning. Needs a sentence written per site, which is an editorial decision. | +Nothing. Every finding is either implemented or carries a recorded decision to leave it alone. --- @@ -396,18 +394,29 @@ pointer to the old name survived that rename as a dangling link until it was fix The pointer now baked into `IERC1643MultiDocument.sol` names the *replacement*, which is one rename away from the same fate — except that this one would be frozen in verified bytecode. -**Verdict: decide.** The fix is not deletion — it is to move the substance in and drop only the -pointer. The three sites differ: -- `DocumentEngineBase.sol:289` — the preceding sentence already states the emission rule in full; the - pointer comes out cleanly. -- `DocumentEngineBase.sol:325` — same, and the `{_removeDocument}` cross-reference is a NatSpec link - that resolves within the source, so it stays. -- `IERC1643MultiDocument.sol:13` — this one *leans* on the document; removing the pointer alone - leaves "the reasoning applies to any … on-chain product" with no statement of what the reasoning - is. A replacement clause has to be written. - -Not implemented because that third site needs an editorial decision about how much of the draft's -rationale belongs in the interface. +**Verdict: implemented — all three pointers removed, and every comment came out shorter.** + +The prediction above was that the third site would need a *replacement clause written in*. That +turned out to be the wrong instinct, and the maintainer's steer — "don't put too many information in +the code" — is the correction: the sentence the pointer propped up was itself the padding. + +| site | before | after | +| --- | --- | --- | +| `DocumentEngineBase._removeDocument` | `…is the token contract's responsibility). See doc/ERCSpecification.` | pointer deleted; the preceding sentence already states the emission rule in full | +| `DocumentEngineBase._setDocument` | `(see {_removeDocument} note and doc/ERCSpecification)` | `(see the {_removeDocument} note)` — the NatSpec link resolves inside the source, so it stays | +| `IERC1643MultiDocument` header | `…(typically a token contract, but the reasoning applies to any ERC-721/ERC-1155 token, vault, or other on-chain product). See doc/ERCSpecification/erc-draft_multi_document_management.md.` | `…the address of the contract the documents belong to — any contract, not only a token.` | + +The third row is the instructive one. The clause enumerating "ERC-721/ERC-1155 token, vault, or other +on-chain product" was gesturing at a rationale that lived in the draft; the *operative* fact for +anyone implementing the interface is simply that `subject` need not be a token. Stating that in one +clause removed the pointer and the enumeration at once — the interface header went from 10 lines +to 9. Nothing was moved into the docs, because nothing needed to be: the draft +already carries the derivation, and it is now reachable only by looking for it, which is correct for +a document that may be renamed again. + +Verified: `grep -rn '\.md\|doc/\|docs/' src/` returns nothing. The two remaining hits repo-wide are in +`test/DocumentEngine.t.sol` and cite this report by bare filename plus a finding ID — the exemption +argued below, deliberately kept. Two exemptions deliberately **not** flagged: mocks and tests (never deployed), and citations of audit records by bare filename (`CLAUDE_ANALYSIS.md` plus a finding ID) — those are immutable historical diff --git a/src/DocumentEngineBase.sol b/src/DocumentEngineBase.sol index 93299c3..fd783f6 100644 --- a/src/DocumentEngineBase.sol +++ b/src/DocumentEngineBase.sol @@ -287,7 +287,7 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen // This engine is a shared, multi-subject manager: per the ERC-1643 // "Emission Responsibility" rules it emits only the address-carrying // extension event (the base `DocumentRemoved` is the token contract's - // responsibility). See doc/ERCSpecification. + // responsibility). emit DocumentRemovedForSubject(subject, name_, doc.uri, doc.documentHash); delete _documents[subject][name_]; @@ -323,7 +323,7 @@ abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, Documen doc.lastModified = block.timestamp; // Shared, multi-subject manager: emit only the address-carrying extension - // event (see {_removeDocument} note and doc/ERCSpecification). + // event (see the {_removeDocument} note). emit DocumentUpdatedForSubject(subject, name_, uri_, documentHash_); } diff --git a/src/interfaces/IERC1643MultiDocument.sol b/src/interfaces/IERC1643MultiDocument.sol index 89e555c..fde3c6a 100644 --- a/src/interfaces/IERC1643MultiDocument.sol +++ b/src/interfaces/IERC1643MultiDocument.sol @@ -8,9 +8,7 @@ pragma solidity ^0.8.24; * @dev Declared **independently of `IERC1643`** (it does not inherit it), so a * shared management contract can implement the address-scoped surface without * being forced to implement the base single-argument functions. `subject` is the - * address of the contract the documents belong to (typically a token contract, - * but the reasoning applies to any ERC-721/ERC-1155 token, vault, or other - * on-chain product). See `doc/ERCSpecification/erc-draft_multi_document_management.md`. + * address of the contract the documents belong to — any contract, not only a token. */ interface IERC1643MultiDocument { /** From 793be81fbd690244b1d4c70bcec952e0c47cfbb5 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:49:03 +0200 Subject: [PATCH 34/47] docs: move CLAUDE_ANALYSIS.md under doc/audits/tools/v0.4.0/claude/ --- AGENTS.md | 5 +++-- CHANGELOG.md | 2 +- CLAUDE.md | 5 +++-- README.md | 1 + doc/audits/AUDIT_OVERVIEW.md | 2 +- doc/audits/tools/v0.4.0/aderyn/aderyn-report.md | 1 + doc/audits/{ => tools/v0.4.0/claude}/CLAUDE_ANALYSIS.md | 6 ++++-- doc/audits/tools/v0.4.0/slither/slither-report.md | 1 + 8 files changed, 15 insertions(+), 8 deletions(-) rename doc/audits/{ => tools/v0.4.0/claude}/CLAUDE_ANALYSIS.md (98%) diff --git a/AGENTS.md b/AGENTS.md index 5f0ae71..21519de 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -168,11 +168,12 @@ Other important files: `doc/audits/` — the security overview (`AUDIT_OVERVIEW.md`) plus versioned static-analysis output under `doc/audits/tools/vX.Y.Z//`, each with a `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging - every finding. Both Aderyn `0.6.5` (0 High · 6 Low) and Slither `0.11.5` + every finding, plus a `claude/` section holding the AI-assisted code-quality review. + Both Aderyn `0.6.5` (0 High · 6 Low) and Slither `0.11.5` (0 High · 1 Med · 1 Low · 2 Info) were run for `v0.4.0` — nothing to fix in either. Slither's dependency filter must be `lib` (Foundry layout); `--filter-paths` fails open, so an entry matching nothing silently pulls the vendored tree into scope. - `doc/audits/CLAUDE_ANALYSIS.md` is the code-quality review (not a security audit) — read + `doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md` is the code-quality review (not a security audit) — read its "left as is" rows before proposing an optimisation: `unchecked {++i}` (0 gas on solc 0.8.34), `string calldata` on the admin `setDocument` (49 gas *worse*), and extracting the duplicated ERC-2771 overrides (impossible — C3 linearization) are all measured dead ends. diff --git a/CHANGELOG.md b/CHANGELOG.md index 273ead6..102dab4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,7 +73,7 @@ for which CMTAT release each version of this engine is built against. - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) [`v3.0.0-rc5`](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) - `foundry.lock` now records every submodule by tag; all five entries had gone stale since `v0.3.0`. - **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) -- **Code-quality review** (`doc/audits/CLAUDE_ANALYSIS.md`) — 14 findings, none a vulnerability. +- **Code-quality review** (`doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md`) — 14 findings, none a vulnerability. Six implemented: - **Gas, `_removeDocumentName`**: the `_documentNames[subject]` mapping slot was re-hashed on every loop iteration; cached as a storage pointer. Measured **−2200 gas** on a 20-entry full scan. diff --git a/CLAUDE.md b/CLAUDE.md index 5f0ae71..21519de 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -168,11 +168,12 @@ Other important files: `doc/audits/` — the security overview (`AUDIT_OVERVIEW.md`) plus versioned static-analysis output under `doc/audits/tools/vX.Y.Z//`, each with a `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging - every finding. Both Aderyn `0.6.5` (0 High · 6 Low) and Slither `0.11.5` + every finding, plus a `claude/` section holding the AI-assisted code-quality review. + Both Aderyn `0.6.5` (0 High · 6 Low) and Slither `0.11.5` (0 High · 1 Med · 1 Low · 2 Info) were run for `v0.4.0` — nothing to fix in either. Slither's dependency filter must be `lib` (Foundry layout); `--filter-paths` fails open, so an entry matching nothing silently pulls the vendored tree into scope. - `doc/audits/CLAUDE_ANALYSIS.md` is the code-quality review (not a security audit) — read + `doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md` is the code-quality review (not a security audit) — read its "left as is" rows before proposing an optimisation: `unchecked {++i}` (0 gas on solc 0.8.34), `string calldata` on the admin `setDocument` (49 gas *worse*), and extracting the duplicated ERC-2771 overrides (impossible — C3 linearization) are all measured dead ends. diff --git a/README.md b/README.md index 5643344..67dc58f 100644 --- a/README.md +++ b/README.md @@ -467,6 +467,7 @@ finding against the source. The security overview is | ------- | ---- | ------ | ------ | ------ | | v0.4.0 | Aderyn `0.6.5` | 0 High · 6 Low — **nothing to fix** | [report](./doc/audits/tools/v0.4.0/aderyn/aderyn-report.md) | [feedback](./doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md) | | v0.4.0 | Slither `0.11.5` | 0 High · 1 Medium · 1 Low · 2 Info — **nothing to fix** | [report](./doc/audits/tools/v0.4.0/slither/slither-report.md) | [feedback](./doc/audits/tools/v0.4.0/slither/slither-report-feedback.md) | +| v0.4.0 | Claude Code (code quality) | 14 findings, **no vulnerability** — 6 implemented, 8 deliberately left | [report](./doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md) | (triage is in the report) | ```bash # Aderyn — mocks excluded (this project's mocks live in test/, which Aderyn does not scan) diff --git a/doc/audits/AUDIT_OVERVIEW.md b/doc/audits/AUDIT_OVERVIEW.md index 40b71ee..0cd60a9 100644 --- a/doc/audits/AUDIT_OVERVIEW.md +++ b/doc/audits/AUDIT_OVERVIEW.md @@ -27,7 +27,7 @@ Out of scope: `lib/` (CMTAT, RuleEngine, OpenZeppelin — audited, or not, upstr | Aderyn `0.6.5` | `v0.4.0` | [report](./tools/v0.4.0/aderyn/aderyn-report.md) | [feedback](./tools/v0.4.0/aderyn/aderyn-report-feedback.md) | | Slither `0.11.5` | `v0.4.0` | [report](./tools/v0.4.0/slither/slither-report.md) | [feedback](./tools/v0.4.0/slither/slither-report-feedback.md) | | ERC conformance analysis (AI-assisted) | `v0.4.0` | open items: [`IMPROVEMENT.md`](../../IMPROVEMENT.md) | — | -| Code-quality review (AI-assisted) | `v0.4.0` | [`CLAUDE_ANALYSIS.md`](./CLAUDE_ANALYSIS.md) — 14 findings, **no vulnerability**; 4 implemented, 2 open decisions | — | +| Code-quality review (AI-assisted) | `v0.4.0` | [`CLAUDE_ANALYSIS.md`](./tools/v0.4.0/claude/CLAUDE_ANALYSIS.md) — 14 findings, **no vulnerability**; 6 implemented, 8 deliberately left, nothing outstanding | — | Both tool runs are against CMTAT `v3.3.0-rc3` and OpenZeppelin `v5.7.0`, with mocks and tests excluded. diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md index b340516..22e5878 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md @@ -23,6 +23,7 @@ > Full triage, with the reasoning verified against each cited line: > [`aderyn-report-feedback.md`](./aderyn-report-feedback.md). > Companion Slither run: [`../slither/slither-report.md`](../slither/slither-report.md). +> Code-quality review (what neither tool can see): [`../claude/CLAUDE_ANALYSIS.md`](../claude/CLAUDE_ANALYSIS.md). > Security overview: [`doc/audits/AUDIT_OVERVIEW.md`](../../../AUDIT_OVERVIEW.md). # Aderyn Analysis Report diff --git a/doc/audits/CLAUDE_ANALYSIS.md b/doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md similarity index 98% rename from doc/audits/CLAUDE_ANALYSIS.md rename to doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md index 10c2b62..6e5fccb 100644 --- a/doc/audits/CLAUDE_ANALYSIS.md +++ b/doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md @@ -17,8 +17,10 @@ > "revocation" could never have been a durable restriction. Its defect is misleading feedback, not > lost containment. > -> For the static-analysis passes see [`AUDIT_OVERVIEW.md`](./AUDIT_OVERVIEW.md); both Aderyn and -> Slither found nothing to fix. This review covers what those tools structurally cannot see. +> For the static-analysis passes see [`aderyn-report.md`](../aderyn/aderyn-report.md) and +> [`slither-report.md`](../slither/slither-report.md); both found nothing to fix, and this review +> covers what those tools structurally cannot see. +> Security overview: [`AUDIT_OVERVIEW.md`](../../../AUDIT_OVERVIEW.md). ## Disposition summary diff --git a/doc/audits/tools/v0.4.0/slither/slither-report.md b/doc/audits/tools/v0.4.0/slither/slither-report.md index 706c40a..a44d9e2 100644 --- a/doc/audits/tools/v0.4.0/slither/slither-report.md +++ b/doc/audits/tools/v0.4.0/slither/slither-report.md @@ -22,6 +22,7 @@ > Full triage, with the reasoning verified against each cited line: > [`slither-report-feedback.md`](./slither-report-feedback.md). > Companion Aderyn run: [`../aderyn/aderyn-report.md`](../aderyn/aderyn-report.md). +> Code-quality review (what neither tool can see): [`../claude/CLAUDE_ANALYSIS.md`](../claude/CLAUDE_ANALYSIS.md). > Security overview: [`doc/audits/AUDIT_OVERVIEW.md`](../../../AUDIT_OVERVIEW.md). **THIS CHECKLIST IS NOT COMPLETE**. Use `--show-ignored-findings` to show all the results. From bb374d7152dd2af634fdcc29dcc6fa013e13a66e Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:55:46 +0200 Subject: [PATCH 35/47] docs: regenerate surya and re-run aderyn/slither after the code-quality changes --- AGENTS.md | 2 +- CHANGELOG.md | 3 + CLAUDE.md | 2 +- README.md | 2 +- doc/audits/AUDIT_OVERVIEW.md | 4 +- .../v0.4.0/aderyn/aderyn-report-feedback.md | 8 +-- .../tools/v0.4.0/aderyn/aderyn-report.md | 16 ++--- .../tools/v0.4.0/claude/CLAUDE_ANALYSIS.md | 25 +++++--- .../v0.4.0/slither/slither-report-feedback.md | 57 ++++++++++++------ .../tools/v0.4.0/slither/slither-report.md | 58 +++++++------------ .../surya_report_DocumentEngine.sol.md | 2 +- .../surya_report_DocumentEngineBase.sol.md | 2 +- ...urya_report_DocumentEngineInvariant.sol.md | 2 +- .../surya_report_DocumentEngineOwnable.sol.md | 2 +- .../surya_report_IERC1643MultiDocument.sol.md | 2 +- .../surya_report_TokenBindingModule.sol.md | 2 +- 16 files changed, 105 insertions(+), 84 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 21519de..5bc269e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -170,7 +170,7 @@ Other important files: `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging every finding, plus a `claude/` section holding the AI-assisted code-quality review. Both Aderyn `0.6.5` (0 High · 6 Low) and Slither `0.11.5` - (0 High · 1 Med · 1 Low · 2 Info) were run for `v0.4.0` — nothing to fix in either. + (0 High · 0 Med · 0 Low · 2 Info) were run for `v0.4.0` — nothing to fix in either. Slither's dependency filter must be `lib` (Foundry layout); `--filter-paths` fails open, so an entry matching nothing silently pulls the vendored tree into scope. `doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md` is the code-quality review (not a security audit) — read diff --git a/CHANGELOG.md b/CHANGELOG.md index 102dab4..c075bf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,9 @@ for which CMTAT release each version of this engine is built against. three times; now read through a storage pointer. A further **−645 gas**. Combined, removal is **−2845 gas (−3.3 %)** worst case. The emit must stay ahead of the `delete` — verified by mutating the order and confirming `testRemoveDocumentEmitsForSubjectEvent` fails. + Side effect: Slither's `incorrect-equality` (Medium) and `timestamp` (Low) stopped firing on the + unchanged `doc.lastModified == 0`, taking it from 4 results to 2. **Not a fix** — both were + already false positives and the detector merely loses the taint through a storage pointer. - **`hasRole` NatSpec**: documented that a role is **unrevokable from the default admin** — `revokeRole` succeeds, emits `RoleRevoked` and drops `getRoleMemberCount`, yet the admin keeps the access. Not a privilege issue (an admin can re-grant itself anything) but the call misreports. diff --git a/CLAUDE.md b/CLAUDE.md index 21519de..5bc269e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -170,7 +170,7 @@ Other important files: `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging every finding, plus a `claude/` section holding the AI-assisted code-quality review. Both Aderyn `0.6.5` (0 High · 6 Low) and Slither `0.11.5` - (0 High · 1 Med · 1 Low · 2 Info) were run for `v0.4.0` — nothing to fix in either. + (0 High · 0 Med · 0 Low · 2 Info) were run for `v0.4.0` — nothing to fix in either. Slither's dependency filter must be `lib` (Foundry layout); `--filter-paths` fails open, so an entry matching nothing silently pulls the vendored tree into scope. `doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md` is the code-quality review (not a security audit) — read diff --git a/README.md b/README.md index 67dc58f..2c72439 100644 --- a/README.md +++ b/README.md @@ -466,7 +466,7 @@ finding against the source. The security overview is | Release | Tool | Result | Report | Triage | | ------- | ---- | ------ | ------ | ------ | | v0.4.0 | Aderyn `0.6.5` | 0 High · 6 Low — **nothing to fix** | [report](./doc/audits/tools/v0.4.0/aderyn/aderyn-report.md) | [feedback](./doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md) | -| v0.4.0 | Slither `0.11.5` | 0 High · 1 Medium · 1 Low · 2 Info — **nothing to fix** | [report](./doc/audits/tools/v0.4.0/slither/slither-report.md) | [feedback](./doc/audits/tools/v0.4.0/slither/slither-report-feedback.md) | +| v0.4.0 | Slither `0.11.5` | 0 High · 0 Medium · 0 Low · 2 Info — **nothing to fix** | [report](./doc/audits/tools/v0.4.0/slither/slither-report.md) | [feedback](./doc/audits/tools/v0.4.0/slither/slither-report-feedback.md) | | v0.4.0 | Claude Code (code quality) | 14 findings, **no vulnerability** — 6 implemented, 8 deliberately left | [report](./doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md) | (triage is in the report) | ```bash diff --git a/doc/audits/AUDIT_OVERVIEW.md b/doc/audits/AUDIT_OVERVIEW.md index 0cd60a9..659f69f 100644 --- a/doc/audits/AUDIT_OVERVIEW.md +++ b/doc/audits/AUDIT_OVERVIEW.md @@ -37,7 +37,7 @@ excluded. | Tool | High | Medium | Low | Info | Anything to fix? | | --- | --- | --- | --- | --- | --- | | Aderyn `0.6.5` | 0 | — | 6 | 0 | **No.** 4 by design, 1 environment, 1 false positive; 1 of the "by design" instances overlaps a known scalability item (§4.7) | -| Slither `0.11.5` | 0 | 1 | 1 | 2 | **No.** All 4 are false positives, reducing to two pieces of code: an existence check (`doc.lastModified == 0`) read as a timestamp comparison, and two `_msgData()` overrides read as dead code | +| Slither `0.11.5` | 0 | 0 | 0 | 2 | **No.** Both are false positives — required `_msgData()` overrides read as dead code. (Was 4 results; the `incorrect-equality`/`timestamp` pair stopped firing when B-2 switched a memory copy to a storage pointer. Nothing was fixed — see the Slither triage.) | Aderyn reports no Medium or Info categories; it classifies only High and Low. @@ -48,6 +48,8 @@ disagree only on what is worth reporting: Slither's highest result (`incorrect-e one Aderyn ignores, and Aderyn's loop advisories draw nothing from Slither. Each dismissal was verified against the cited line; the `_msgData()` "dead code" was verified by deleting it and confirming the compile fails (`Error (6480): Derived contract must override function "_msgData"`). +Slither's highest result at the time was `incorrect-equality` (Medium); it no longer fires, but as a +detector artefact rather than a fix — the triage explains why. Note the standing limitation: neither tool can see the specification-level issues that matter most for this engine — those are tracked as open items below. diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md index 01be8a8..07aa4a2 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md @@ -8,7 +8,7 @@ | Scope | `src/` only — 9 files, 307 nSLOC. **Mocks and tests excluded.** This project keeps its mocks (`CMTATDocumentEngineMock`, `OpenDocumentEngine`) inside `test/DocumentEngine.t.sol`, which Aderyn does not scan, so `-x mocks` matched nothing and changed nothing. | | Dependencies | CMTAT `v3.3.0-rc3` (`658672f1`), OpenZeppelin `v5.7.0` (`cab19933`) | | Result | **0 High · 6 Low** | -| Companion | [`../slither/slither-report.md`](../slither/slither-report.md) — Slither `0.11.5`, 4 results, also nothing to fix | +| Companion | [`../slither/slither-report.md`](../slither/slither-report.md) — Slither `0.11.5`, 2 results, also nothing to fix | ## Executive triage @@ -17,7 +17,7 @@ Five of the six are the analyzer's standing advisories about deliberate design choices (a privileged operator, a caret pragma, PUSH0, revert-in-loop, storage-writes-in-loop) and one is a false positive. -The one result worth keeping in view is **L-5 at `DocumentEngineBase.sol:264`**, which is not a batch +The one result worth keeping in view is **L-5 at `DocumentEngineBase.sol:265`**, which is not a batch loop but the linear scan in `_removeDocumentName`. Aderyn reached it from the "costly operation in a loop" heuristic; it happens to land on the same code as `IMPROVEMENT.md` item 4, which flags the O(n) removal against the multi-subject draft's expectation of "index tracking to support O(1) removals". @@ -35,7 +35,7 @@ CMTAT upgrade. | L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** | Every file uses a caret pragma, intentionally, so the sources stay consumable as a library by projects on a different `0.8.x`; the compiler actually used for the deployed bytecode is pinned to `0.8.34` in `foundry.toml`, and `foundry.lock` pins every dependency. Verified: no file uses a construct that behaves differently across the allowed range. The floor is now **`^0.8.24`**, raised from `^0.8.20` after the previous run: `^0.8.20` over-promised, because `AccessControlEnumerable.sol` and `EnumerableSet.sol` were already `^0.8.24` and CMTAT `v3.3.0-rc3` moved `draft-IERC1643.sol` there too — no compiler in `0.8.20`–`0.8.23` could build the tree. `0.8.24` is the true `src/` floor; the full project including the CMTAT-importing tests needs `0.8.27`, because `require(cond, CustomError())` is legacy-pipeline-only from that version on. | | L-3 | PUSH0 Opcode | Low | 9 | **Environment** | Consequence of the caret pragma plus `evm_version = prague`: the compiler emits `PUSH0`, which is unavailable on chains that have not adopted Shanghai. Not a source defect. A deployer targeting such a chain must lower `evm_version` in `foundry.toml` — but CMTAT v3 itself requires `prague`, so that configuration is out of scope for this engine. | | L-4 | Loop Contains `require`/`revert` | Low | 4 | **By design** | `DocumentEngineBase.sol:118, 141, 158, 175` — the four batch loops. The reverts are raised inside `_setDocument` / `_removeDocument` (`ERC1643InvalidName`, `MultiDocumentInvalidSubject`, `ERC1643MissingDocument`). Batch operations are deliberately **all-or-nothing**: a batch containing one bad entry must not half-apply, since partial application would leave the operator unable to tell which documents were written without re-reading every entry. Skipping bad entries instead would silently drop them. | -| L-5 | Costly operations inside loop | Low | 5 | **By design** ×4, **known item** ×1 | Four instances (`:118, 141, 158, 175`) are storage writes in the batch loops — unavoidable, and the reason the batch functions exist is to amortise the 21 000-gas transaction overhead across those writes. The fifth (`:264`) is `_removeDocumentName`'s linear scan with swap-and-pop; see the triage note above and `IMPROVEMENT.md` item 4. | +| L-5 | Costly operations inside loop | Low | 5 | **By design** ×4, **known item** ×1 | Four instances (`:118, 141, 158, 175`) are storage writes in the batch loops — unavoidable, and the reason the batch functions exist is to amortise the 21 000-gas transaction overhead across those writes. The fifth (`:265`) is `_removeDocumentName`'s linear scan with swap-and-pop; see the triage note above and `IMPROVEMENT.md` item 4. | | L-6 | Unchecked Return | Low | 1 | **False positive** | `DocumentEngine.sol:43`, `_grantRole(DEFAULT_ADMIN_ROLE, admin);`. OpenZeppelin's `_grantRole` returns `false` only when the account already holds the role. This call is in the constructor of a freshly deployed contract, where no role has been granted yet, so it always returns `true`; `admin == address(0)` is already rejected on the preceding lines. There is no state to check and no recovery path to take. | ## Delta @@ -78,7 +78,7 @@ Two null results worth recording, because they are easy to misread as "not analy None — `v0.4.0` is the **first** release with static analysis recorded. `doc/audits/` did not exist before it. Future runs should diff against this one. -**Slither has now been run** (`0.11.5`, 4 results, nothing to fix) — see +**Slither has now been run** (`0.11.5`, 2 results, nothing to fix) — see [`../slither/slither-report-feedback.md`](../slither/slither-report-feedback.md). This closes the gap flagged here previously, so the next release can diff both tools. The two disagree on what is worth reporting: Slither raised an existence-check equality and two required `_msgData()` overrides diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md index 22e5878..cd09669 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md @@ -17,13 +17,15 @@ > | L-5 | Costly operations inside loop | Low | 5 | **By design** (4 batch loops) + **1 known item** — `_removeDocumentName` is O(n), see `IMPROVEMENT.md` item 4 | > | L-6 | Unchecked Return | Low | 1 | **False positive** — `_grantRole` in a constructor on a fresh contract cannot return `false` | > -> Unchanged from the previous `v0.4.0` run (CMTAT `v3.3.0-rc2`, OZ `v5.6.1`): same six detectors, -> same instance counts, same lines. See the feedback file's Delta section. +> Re-run after the code-quality review (`../claude/CLAUDE_ANALYSIS.md`). **Unchanged**: same six +> detectors, same instance counts (2/9/9/4/5/1). One line moved — L-5's fifth instance is now +> `DocumentEngineBase.sol:265`, because finding B-1 inserted a storage-pointer line above the loop. +> Notably the `virtual` keywords added by E-1 produced no new finding. > > Full triage, with the reasoning verified against each cited line: > [`aderyn-report-feedback.md`](./aderyn-report-feedback.md). > Companion Slither run: [`../slither/slither-report.md`](../slither/slither-report.md). -> Code-quality review (what neither tool can see): [`../claude/CLAUDE_ANALYSIS.md`](../claude/CLAUDE_ANALYSIS.md). +> Code-quality review: [`../claude/CLAUDE_ANALYSIS.md`](../claude/CLAUDE_ANALYSIS.md). > Security overview: [`doc/audits/AUDIT_OVERVIEW.md`](../../../AUDIT_OVERVIEW.md). # Aderyn Analysis Report @@ -51,7 +53,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Key | Value | | --- | --- | | .sol Files | 9 | -| Total nSLOC | 310 | +| Total nSLOC | 312 | ## Files Details @@ -59,7 +61,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Filepath | nSLOC | | --- | --- | | src/DocumentEngine.sol | 54 | -| src/DocumentEngineBase.sol | 149 | +| src/DocumentEngineBase.sol | 151 | | src/DocumentEngineInvariant.sol | 5 | | src/DocumentEngineOwnable.sol | 29 | | src/interfaces/IERC1643MultiDocument.sol | 13 | @@ -67,7 +69,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | src/interfaces/ITokenBinding.sol | 8 | | src/modules/TokenBindingModule.sol | 36 | | src/modules/VersionModule.sol | 12 | -| **Total** | **310** | +| **Total** | **312** | ## Issue Summary @@ -299,7 +301,7 @@ Invoking `SSTORE` operations in loops may waste gas. Use a local variable to hol for (uint256 i = 0; i < length; ++i) { ``` -- Found in src/DocumentEngineBase.sol [Line: 264](../../../../../src/DocumentEngineBase.sol#L264) +- Found in src/DocumentEngineBase.sol [Line: 265](../../../../../src/DocumentEngineBase.sol#L265) ```solidity for (uint256 i = 0; i < length; ++i) { diff --git a/doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md b/doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md index 6e5fccb..ea9fec0 100644 --- a/doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md +++ b/doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md @@ -31,15 +31,15 @@ | B-1 | Mapping slot re-hashed every iteration in `_removeDocumentName` | ✅ fixed, **−2200 gas** | `DocumentEngineBase.sol:263` | | B-2 | `_removeDocument` copied the whole `Document` (incl. the URI) to memory | ✅ fixed, **−645 gas** | `DocumentEngineBase.sol:281` | | C-1 | Every event has exactly one emit site | ⬜ nothing to do — verified good | — | -| C-2 | Trusted forwarder set at construction without an event | ⬜ left as is | `DocumentEngine.sol:40` | -| D-1 | ERC-2771 trio duplicated byte-for-byte across both deployments | ⬜ left as is — **extraction proven impossible** | `DocumentEngine.sol:137`, `DocumentEngineOwnable.sol:73` | +| C-2 | Trusted forwarder set at construction without an event | ⬜ left as is | `DocumentEngine.sol:39` | +| D-1 | ERC-2771 trio duplicated byte-for-byte across both deployments | ⬜ left as is — **extraction proven impossible** | `DocumentEngine.sol:146`, `DocumentEngineOwnable.sol:73` | | E-1 | `virtual` coverage inconsistent between the two modules | ✅ fixed — all 12 internal functions now `virtual` | `DocumentEngineBase.sol`, `TokenBindingModule.sol`, both deployments | -| F-1 | ERC-165 interface IDs — no inherited-selector trap | ⬜ nothing to do — verified correct | `DocumentEngine.sol:115` | +| F-1 | ERC-165 interface IDs — no inherited-selector trap | ⬜ nothing to do — verified correct | `DocumentEngine.sol:114` | | G-1 | `DocumentEngineInvariant` comment misattributes `NotBoundToken` | ✅ fixed | `DocumentEngineInvariant.sol` | | G-2 | Contracts point at documentation paths that have already moved once | ✅ fixed — all 3 pointers removed, comments got *shorter* | `DocumentEngineBase.sol`, `IERC1643MultiDocument.sol` | | G-3 | NatSpec block-length distribution is healthy | ⬜ nothing to do — measured | — | -| H-1 | A role cannot be revoked from the default admin, but the call succeeds | ✅ documented + regression test | `DocumentEngine.sol:73` | -| H-2 | Caller-scoped reads return an empty namespace instead of reverting | ⬜ left as is — already documented and tested | `DocumentEngineBase.sol:186` | +| H-1 | A role cannot be revoked from the default admin, but the call succeeds | ✅ documented + regression test | `DocumentEngine.sol:72` | +| H-2 | Caller-scoped reads return an empty namespace instead of reverting | ⬜ left as is — already documented and tested | `DocumentEngineBase.sol:190` | Rows: 14. Fixed: 6. Left deliberately: 8. Open decisions: none. @@ -170,6 +170,15 @@ The guard is real. Ordering restored, 73/73 passing. **Verdict: implemented.** Storage layout re-checked from the compiled artifacts afterwards (`--extra-output storageLayout`, 5 non-empty entries per deployment, unchanged). +**Side effect worth flagging, because it is easy to misread as a win.** Re-running Slither after this +change, its two highest findings — `incorrect-equality` (Medium) and `timestamp` (Low), both on this +exact line — **stopped firing**, taking its total from 4 results to 2. Nothing was fixed: the +comparison is character-for-character identical, and both were already triaged as false positives on +their merits. Slither classifies `lastModified` as timestamp-derived when it arrives via a memory +copy of the struct and loses that classification through a storage pointer. The Slither triage +records this as a detector artefact rather than a remediation, so nobody later reads the drop as a +Medium having been closed. + ## C. Events ### C-1. Single emit site per event — verified, nothing to do @@ -193,7 +202,7 @@ and a future refactor should preserve it. ### C-2. The trusted forwarder is not evented at construction -`DocumentEngine.sol:40` / `DocumentEngineOwnable.sol:32` pass `forwarderIrrevocable` to +`DocumentEngine.sol:39` / `DocumentEngineOwnable.sol:31` pass `forwarderIrrevocable` to `ERC2771Context` and emit nothing, so a log-only indexer never sees the value. That matters more than usual here because the forwarder can act as any bound subject (`IMPROVEMENT.md` item 5). @@ -209,7 +218,7 @@ read. ### D-1. The ERC-2771 trio is byte-identical across both deployments — and cannot be shared -`DocumentEngine.sol:137-157` and `DocumentEngineOwnable.sol:73-93` contain `_msgSender`, `_msgData` +`DocumentEngine.sol:146-165` and `DocumentEngineOwnable.sol:73-92` contain `_msgSender`, `_msgData` and `_contextSuffixLength`, **10 code lines each** (NatSpec excluded), byte-for-byte identical (`diff` confirms). @@ -451,7 +460,7 @@ something next to a median of 6 — and here the ratio is defensible. ### H-1. Revoking a role from the default admin succeeds but removes nothing — **documented** -`DocumentEngine.sol:73` overrides `hasRole` so that `DEFAULT_ADMIN_ROLE` implicitly holds every role. +`DocumentEngine.sol:72` overrides `hasRole` so that `DEFAULT_ADMIN_ROLE` implicitly holds every role. The existing NatSpec documented one consequence (the enumeration mismatch). It did not document this one, which I verified by running it: diff --git a/doc/audits/tools/v0.4.0/slither/slither-report-feedback.md b/doc/audits/tools/v0.4.0/slither/slither-report-feedback.md index a79f44f..6f511e5 100644 --- a/doc/audits/tools/v0.4.0/slither/slither-report-feedback.md +++ b/doc/audits/tools/v0.4.0/slither/slither-report-feedback.md @@ -7,36 +7,57 @@ | Tool version | `slither 0.11.5` | | Scope | `src/` only — 28 contracts analysed with 101 detectors. **Mocks and tests excluded.** This project keeps its mocks (`CMTATDocumentEngineMock`, `OpenDocumentEngine`) inside `test/DocumentEngine.t.sol`, which the `test` filter removes; there is no `src/mocks`, so the `mocks` filter entry matched nothing. | | Dependencies | CMTAT `v3.3.0-rc3` (`658672f1`), OpenZeppelin `v5.7.0` (`cab19933`) | -| Result | **0 High · 1 Medium · 1 Low · 2 Informational** (4 results) | +| Result | **0 High · 0 Medium · 0 Low · 2 Informational** (2 results) — was 4; see the correction below | ## Executive triage **Nothing to fix.** No finding is exploitable, and none blocks the `v0.4.0` release. -All four results reduce to two underlying pieces of code, and both are correct as written: +Both remaining results are the same thing: **`dead-code` ×2** flags the `_msgData()` overrides. +These are not dead — Solidity **requires** them. Verified by deleting one and compiling: +`Error (6480): Derived contract must override function "_msgData". Two or more base classes define +function with same name and parameter types.` -- **Two detectors (`incorrect-equality`, `timestamp`) fire on the same line** — - `DocumentEngineBase.sol:282`, `doc.lastModified == 0`. Both misread an *existence sentinel* as a - *time comparison*. See below; neither detector has a notion of "zero means absent". -- **`dead-code` ×2** flags the `_msgData()` overrides. These are not dead — Solidity **requires** - them. Verified by deleting one and compiling: `Error (6480): Derived contract must override - function "_msgData". Two or more base classes define function with same name and parameter types.` +### Correction — two findings disappeared, and not because anything was fixed -The Medium severity on `incorrect-equality` deserves a word, because it is the highest-severity -result either tool produced for this release and it is worth being explicit that it is not real. -Slither's detector targets strict equality against a *quantity that can step past the compared -value* — a balance that can be donated to, or a timestamp compared with `==` where a block can skip -the exact second. Neither shape applies here: `0` is not a point on a timeline the value passes -through, it is the default of an unwritten struct. +The previous run of this report carried two further results, both on +`_removeDocument`'s `doc.lastModified == 0`: + +| ID | Detector | Sev | Status now | +| --- | --- | --- | --- | +| (was ID-0) | `incorrect-equality` | Medium | **No longer reported** | +| (was ID-1) | `timestamp` | Low | **No longer reported** | + +They stopped firing when the code-quality review's finding B-2 changed +`Document memory doc = _documents[subject][name_]` to `Document storage doc = …` — a gas +optimisation that left the comparison character-for-character identical. Slither's taint tracking +classifies `lastModified` as timestamp-derived when it arrives via a memory copy of the struct, and +apparently loses that classification when the field is read through a storage pointer. + +**Nothing was fixed.** The original triage (retained below) established both as false positives on +their merits; their disappearance is a detector artefact, not an improvement, and the same reasoning +would apply verbatim if a future Slither release started reporting them again. Recorded here rather +than deleted, because a reader comparing "4 results" against "2 results" across the two runs would +otherwise conclude a Medium had been remediated. + +**The original triage, still the operative reasoning if these ever return.** Slither's +`incorrect-equality` detector targets strict equality against a *quantity that can step past the +compared value* — a balance that can be donated to, or a timestamp compared with `==` where a block +can skip the exact second. Neither shape applies: `0` is not a point on a timeline the value passes +through, it is the default of an unwritten struct. `lastModified` is only ever assigned +`block.timestamp`, which is non-zero on every live chain, so a *stored* document can never read back +as `0`; the comparison is a total existence test, and the ERC-1643 spec requires the +revert-on-missing behaviour it implements. Covered by `testCannotRemoveMissingDocument`. The +`timestamp` detector's concern — a validator nudging `block.timestamp` to flip a branch — needs an +ordering comparison; there is none here, and no achievable manipulation sets `block.timestamp` to +`0`. ## Findings | ID | Detector | Sev | Conf | Instances | Disposition | Reason (verified against the cited lines) | | --- | --- | --- | --- | --- | --- | --- | -| ID-0 | `incorrect-equality` | Medium | High | 1 | **False positive** | `DocumentEngineBase.sol:282`, inside `_removeDocument`: `if (doc.lastModified == 0) revert ERC1643MissingDocument();`. `lastModified` is only ever assigned `block.timestamp` (`:322`), which is non-zero on every live chain, so a *stored* document can never read back as `0`. The comparison is therefore a total existence test — the same idiom `_setDocument` uses at `:316` to detect a new name. The ERC-1643 spec requires the revert-on-missing behaviour this line implements. Making it `<= 0` or a range check, as the detector suggests, would change nothing and read worse. Covered by `testCannotRemoveMissingDocument` (`test/DocumentEngine.t.sol:450`). | -| ID-1 | `timestamp` | Low | Medium | 1 | **False positive** | Same line as ID-0. The detector flags any use of a timestamp in a comparison, on the theory that a validator can nudge `block.timestamp` by a few seconds and flip a branch. There is no ordering comparison here — no `<`, `>`, or deadline — only equality against the `0` sentinel. A validator cannot set `block.timestamp` to `0`, so no achievable manipulation changes which branch is taken. The stored value is metadata surfaced by `getDocument`; nothing in the engine makes a decision based on how recent it is. | -| ID-2 | `dead-code` | Info | Medium | 1 | **False positive — required override** | `DocumentEngine.sol:146-148`, `_msgData()`. `DocumentEngine` inherits `Context` through two paths (`AccessControlEnumerable` → `AccessControl` → `Context`, and `ERC2771Context` → `Context`), and `ERC2771Context` overrides `_msgData()`. Solidity therefore demands an explicit `override(ERC2771Context, Context)` in the derived contract. **Verified empirically:** removing the function fails to compile with `Error (6480): Derived contract must override function "_msgData"`. Slither reports it "never used" because nothing in this project calls `_msgData()` directly — but it is what makes ERC-2771 calldata handling correct for any inherited code that does. | -| ID-3 | `dead-code` | Info | Medium | 1 | **False positive — required override** | `DocumentEngineOwnable.sol:82-84`. Identical to ID-2, via `Ownable2Step` → `Ownable` → `Context` and `ERC2771Context` → `Context`. | +| ID-0 | `dead-code` | Info | Medium | 1 | **False positive — required override** | `DocumentEngine.sol:155-157`, `_msgData()`. `DocumentEngine` inherits `Context` through two paths (`AccessControlEnumerable` → `AccessControl` → `Context`, and `ERC2771Context` → `Context`), and `ERC2771Context` overrides `_msgData()`. Solidity therefore demands an explicit `override(ERC2771Context, Context)` in the derived contract. **Verified empirically:** removing the function fails to compile with `Error (6480): Derived contract must override function "_msgData"`. Slither reports it "never used" because nothing in this project calls `_msgData()` directly — but it is what makes ERC-2771 calldata handling correct for any inherited code that does. | +| ID-1 | `dead-code` | Info | Medium | 1 | **False positive — required override** | `DocumentEngineOwnable.sol:82-84`. Identical to ID-0, via `Ownable2Step` → `Ownable` → `Context` and `ERC2771Context` → `Context`. | ## What Slither did *not* flag diff --git a/doc/audits/tools/v0.4.0/slither/slither-report.md b/doc/audits/tools/v0.4.0/slither/slither-report.md index a44d9e2..7278cdc 100644 --- a/doc/audits/tools/v0.4.0/slither/slither-report.md +++ b/doc/audits/tools/v0.4.0/slither/slither-report.md @@ -4,63 +4,47 @@ > | --- | --- | > | Command | `slither . --checklist --filter-paths "node_modules,lib,test,forge-std,mocks"` | > | Tool version | `slither 0.11.5` | -> | Scope | `src/` only — 28 contracts analysed with 101 detectors (the count includes inherited OpenZeppelin/CMTAT contracts pulled in by the compiler; findings are filtered to project sources). **Mocks/tests excluded** — this project's mocks (`CMTATDocumentEngineMock`, `OpenDocumentEngine`) live in `test/DocumentEngine.t.sol`, which the `test` filter removes. | -> | Result | **0 High · 1 Medium · 1 Low · 2 Informational** (4 results) | -> | Verdict | **Nothing to fix.** Two false positives on one existence check, and two required Solidity overrides misread as dead code. | +> | Scope | `src/` only — 28 contracts analysed with 101 detectors (the count includes inherited OpenZeppelin/CMTAT contracts pulled in by the compiler; findings are filtered to project sources). **Mocks/tests excluded** — this project's mocks (`CMTATDocumentEngineMock`, `OpenDocumentEngine`, `OverridingDocumentEngine`) live in `test/DocumentEngine.t.sol`, which the `test` filter removes. | +> | Result | **0 High · 0 Medium · 0 Low · 2 Informational** (2 results) | +> | Verdict | **Nothing to fix.** Both results are required Solidity overrides misread as dead code. | > > | Detector | Severity | Confidence | Instances | Assessment | > | --- | --- | --- | --- | --- | -> | `incorrect-equality` | Medium | High | 1 | **False positive** — `doc.lastModified == 0` is an existence sentinel, not a threshold comparison | -> | `timestamp` | Low | Medium | 1 | **False positive** — same line; equality against `0`, no miner-influenceable ordering | > | `dead-code` | Informational | Medium | 2 | **False positive** — `_msgData()` is a *mandatory* override; removing it fails to compile (verified) | > -> **Scope check:** `grep -c 'lib/\|node_modules/'` over this report returns **0** — no dependency -> code is in scope. This is a Foundry project, so the dependency filter entry is `lib`; note this -> differs from the command previously documented in the README, which listed individual submodule -> names and would have left `lib/RuleEngine` unfiltered. +> **Changed since the previous run — read this before comparing counts.** This report has 2 results +> where the previous run had 4. The `incorrect-equality` (Medium) and `timestamp` (Low) findings on +> `_removeDocument`'s `doc.lastModified == 0` no longer fire, because the code-quality review's +> finding B-2 changed `Document memory doc` to `Document storage doc`. **Nothing was fixed by that** — +> the comparison is character-for-character the same and was a false positive to begin with (see the +> feedback file). Slither's taint tracking simply stops classifying the value as timestamp-derived +> when it is read through a storage pointer instead of a memory copy. Do not read the drop from 4 to +> 2 as a security improvement; it is a detector artefact. +> +> **Scope check:** `grep -c 'lib/\|node_modules/'` over the tool output below returns **0** — no +> dependency code is in scope. This is a Foundry project, so the dependency filter entry is `lib`; +> note this differs from the command previously documented in the README, which listed individual +> submodule names and would have left `lib/RuleEngine` unfiltered. > > Full triage, with the reasoning verified against each cited line: > [`slither-report-feedback.md`](./slither-report-feedback.md). > Companion Aderyn run: [`../aderyn/aderyn-report.md`](../aderyn/aderyn-report.md). -> Code-quality review (what neither tool can see): [`../claude/CLAUDE_ANALYSIS.md`](../claude/CLAUDE_ANALYSIS.md). +> Code-quality review: [`../claude/CLAUDE_ANALYSIS.md`](../claude/CLAUDE_ANALYSIS.md). > Security overview: [`doc/audits/AUDIT_OVERVIEW.md`](../../../AUDIT_OVERVIEW.md). **THIS CHECKLIST IS NOT COMPLETE**. Use `--show-ignored-findings` to show all the results. Summary - - [incorrect-equality](#incorrect-equality) (1 results) (Medium) - - [timestamp](#timestamp) (1 results) (Low) - [dead-code](#dead-code) (2 results) (Informational) -## incorrect-equality -Impact: Medium -Confidence: High - - [ ] ID-0 -[DocumentEngineBase._removeDocument(address,bytes32)](src/DocumentEngineBase.sol#L279-L294) uses a dangerous strict equality: - - [doc.lastModified == 0](src/DocumentEngineBase.sol#L282) - -src/DocumentEngineBase.sol#L279-L294 - - -## timestamp -Impact: Low -Confidence: Medium - - [ ] ID-1 -[DocumentEngineBase._removeDocument(address,bytes32)](src/DocumentEngineBase.sol#L279-L294) uses timestamp for comparisons - Dangerous comparisons: - - [doc.lastModified == 0](src/DocumentEngineBase.sol#L282) - -src/DocumentEngineBase.sol#L279-L294 - - ## dead-code Impact: Informational Confidence: Medium - - [ ] ID-2 -[DocumentEngine._msgData()](src/DocumentEngine.sol#L146-L148) is never used and should be removed + - [ ] ID-0 +[DocumentEngine._msgData()](src/DocumentEngine.sol#L155-L157) is never used and should be removed -src/DocumentEngine.sol#L146-L148 +src/DocumentEngine.sol#L155-L157 - - [ ] ID-3 + - [ ] ID-1 [DocumentEngineOwnable._msgData()](src/DocumentEngineOwnable.sol#L82-L84) is never used and should be removed src/DocumentEngineOwnable.sol#L82-L84 diff --git a/doc/surya/surya_report/surya_report_DocumentEngine.sol.md b/doc/surya/surya_report/surya_report_DocumentEngine.sol.md index 6192354..b2c75ec 100644 --- a/doc/surya/surya_report/surya_report_DocumentEngine.sol.md +++ b/doc/surya/surya_report/surya_report_DocumentEngine.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| ./DocumentEngine.sol | b5712711a9a13e254b60044029edb4b66fc06213 | +| ./DocumentEngine.sol | f94dc434fc6173eace632f1f3c70857c1b28dd69 | ### Contracts Description Table diff --git a/doc/surya/surya_report/surya_report_DocumentEngineBase.sol.md b/doc/surya/surya_report/surya_report_DocumentEngineBase.sol.md index 4127471..72af896 100644 --- a/doc/surya/surya_report/surya_report_DocumentEngineBase.sol.md +++ b/doc/surya/surya_report/surya_report_DocumentEngineBase.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| ./DocumentEngineBase.sol | d3de1fa1ff079856dc1cfe0f19ee8206e99a1fb1 | +| ./DocumentEngineBase.sol | 2c5a7b3ace8bd7e83ea19b1d53dc833fc4ec5349 | ### Contracts Description Table diff --git a/doc/surya/surya_report/surya_report_DocumentEngineInvariant.sol.md b/doc/surya/surya_report/surya_report_DocumentEngineInvariant.sol.md index bb74e0f..20c75e4 100644 --- a/doc/surya/surya_report/surya_report_DocumentEngineInvariant.sol.md +++ b/doc/surya/surya_report/surya_report_DocumentEngineInvariant.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| ./DocumentEngineInvariant.sol | 5c2227d3902c20320c2a74588eebaf8f5b562a33 | +| ./DocumentEngineInvariant.sol | c6d759bcf18c530f78db5ec5cebc7ddd7b715ff3 | ### Contracts Description Table diff --git a/doc/surya/surya_report/surya_report_DocumentEngineOwnable.sol.md b/doc/surya/surya_report/surya_report_DocumentEngineOwnable.sol.md index 63f8872..76b793a 100644 --- a/doc/surya/surya_report/surya_report_DocumentEngineOwnable.sol.md +++ b/doc/surya/surya_report/surya_report_DocumentEngineOwnable.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| ./DocumentEngineOwnable.sol | 7604130e6a1c6390643293f8afba1c3b49293244 | +| ./DocumentEngineOwnable.sol | a58193c6ae7f706de6e47a10dab38ca78cea208f | ### Contracts Description Table diff --git a/doc/surya/surya_report/surya_report_IERC1643MultiDocument.sol.md b/doc/surya/surya_report/surya_report_IERC1643MultiDocument.sol.md index 6f4378a..4ae84c1 100644 --- a/doc/surya/surya_report/surya_report_IERC1643MultiDocument.sol.md +++ b/doc/surya/surya_report/surya_report_IERC1643MultiDocument.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| ./interfaces/IERC1643MultiDocument.sol | 2ddea154d4764f7f0edb35a85dc8df6db72604e0 | +| ./interfaces/IERC1643MultiDocument.sol | ebf7a8b0cc52897f5caefe51d12a4ec7b63cfa38 | ### Contracts Description Table diff --git a/doc/surya/surya_report/surya_report_TokenBindingModule.sol.md b/doc/surya/surya_report/surya_report_TokenBindingModule.sol.md index f0b2b23..f9b4546 100644 --- a/doc/surya/surya_report/surya_report_TokenBindingModule.sol.md +++ b/doc/surya/surya_report/surya_report_TokenBindingModule.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| ./modules/TokenBindingModule.sol | 5374117f2b0ecde1d5a590d6e30c74386de1a0fc | +| ./modules/TokenBindingModule.sol | af09f7e1f947e9bb4fcef227a1be44d97159d0d7 | ### Contracts Description Table From 0ae9a274b3e9ed3137a886aeac99e162ba366508 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:59:21 +0200 Subject: [PATCH 36/47] chore: regenerate coverage for v0.4.0 and correct the release checklist path --- CHANGELOG.md | 6 +- doc/coverage/coverage/index-sort-f.html | 60 +-- doc/coverage/coverage/index-sort-l.html | 60 +-- doc/coverage/coverage/index.html | 60 +-- .../src/DocumentEngine.sol.func-sort-c.html | 73 +-- .../coverage/src/DocumentEngine.sol.func.html | 73 +-- .../coverage/src/DocumentEngine.sol.gcov.html | 485 +++++++----------- .../DocumentEngineBase.sol.func-sort-c.html | 152 ++++++ .../src/DocumentEngineBase.sol.func.html | 152 ++++++ .../src/DocumentEngineBase.sol.gcov.html | 439 ++++++++++++++++ ...DocumentEngineOwnable.sol.func-sort-c.html | 92 ++++ .../src/DocumentEngineOwnable.sol.func.html | 92 ++++ .../src/DocumentEngineOwnable.sol.gcov.html | 169 ++++++ doc/coverage/coverage/src/index-sort-f.html | 68 +-- doc/coverage/coverage/src/index-sort-l.html | 68 +-- doc/coverage/coverage/src/index.html | 68 +-- .../TokenBindingModule.sol.func-sort-c.html | 96 ++++ .../modules/TokenBindingModule.sol.func.html | 96 ++++ .../modules/TokenBindingModule.sol.gcov.html | 169 ++++++ .../VersionModule.sol.func-sort-c.html | 80 +++ .../src/modules/VersionModule.sol.func.html | 80 +++ .../src/modules/VersionModule.sol.gcov.html | 116 +++++ .../coverage/src/modules/index-sort-f.html | 103 ++++ .../coverage/src/modules/index-sort-l.html | 103 ++++ doc/coverage/coverage/src/modules/index.html | 103 ++++ doc/coverage/lcov.info | 421 ++++++++------- 26 files changed, 2692 insertions(+), 792 deletions(-) create mode 100644 doc/coverage/coverage/src/DocumentEngineBase.sol.func-sort-c.html create mode 100644 doc/coverage/coverage/src/DocumentEngineBase.sol.func.html create mode 100644 doc/coverage/coverage/src/DocumentEngineBase.sol.gcov.html create mode 100644 doc/coverage/coverage/src/DocumentEngineOwnable.sol.func-sort-c.html create mode 100644 doc/coverage/coverage/src/DocumentEngineOwnable.sol.func.html create mode 100644 doc/coverage/coverage/src/DocumentEngineOwnable.sol.gcov.html create mode 100644 doc/coverage/coverage/src/modules/TokenBindingModule.sol.func-sort-c.html create mode 100644 doc/coverage/coverage/src/modules/TokenBindingModule.sol.func.html create mode 100644 doc/coverage/coverage/src/modules/TokenBindingModule.sol.gcov.html create mode 100644 doc/coverage/coverage/src/modules/VersionModule.sol.func-sort-c.html create mode 100644 doc/coverage/coverage/src/modules/VersionModule.sol.func.html create mode 100644 doc/coverage/coverage/src/modules/VersionModule.sol.gcov.html create mode 100644 doc/coverage/coverage/src/modules/index-sort-f.html create mode 100644 doc/coverage/coverage/src/modules/index-sort-l.html create mode 100644 doc/coverage/coverage/src/modules/index.html diff --git a/CHANGELOG.md b/CHANGELOG.md index c075bf1..c822893 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,7 +35,11 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) > forge fmt - Documentation - - Perform a code coverage and update the files in the corresponding directory [./doc/general/test/coverage](./doc/general/test/coverage) + - Perform a code coverage and update the files in the corresponding directory [./doc/coverage](./doc/coverage) + (`forge coverage --report lcov --report-file /tmp/lcov-full.info`, then + `lcov --extract /tmp/lcov-full.info 'src/*' -o doc/coverage/lcov.info` and + `genhtml doc/coverage/lcov.info --output-directory doc/coverage/coverage`; the `src/*` filter keeps + `test/` and `script/` out of the published figure) - Perform an audit with several audit tools (Aderyn and Slither), update the report in the corresponding directory [./doc/audits/tools](./doc/audits/tools) - Update surya doc by running the 3 scripts in [./doc/script](./doc/script) diff --git a/doc/coverage/coverage/index-sort-f.html b/doc/coverage/coverage/index-sort-f.html index 7e88c84..08e80aa 100644 --- a/doc/coverage/coverage/index-sort-f.html +++ b/doc/coverage/coverage/index-sort-f.html @@ -31,27 +31,18 @@ lcov.info Lines: - 51 - 52 - 98.1 % + 125 + 131 + 95.4 % Date: - 2024-09-09 15:02:57 + 2026-08-17 11:57:58 Functions: - 12 - 14 - 85.7 % - - - - - - Branches: - 13 - 14 - 92.9 % + 36 + 40 + 90.0 % @@ -65,33 +56,38 @@ - - - - - - - - + + + + + + - + + + + + + + + - - - - - - + + + +


Directory Sort by name Line Coverage Sort by line coverage Functions Sort by function coverageBranches Sort by branch coverage
src -
98.1%98.1%
+
94.4%94.4%
+
94.4 %101 / 10787.5 %28 / 32
src/modules +
100.0%
98.1 %51 / 5285.7 %12 / 1492.9 %13 / 14100.0 %24 / 24100.0 %8 / 8
diff --git a/doc/coverage/coverage/index-sort-l.html b/doc/coverage/coverage/index-sort-l.html index 00b9318..8a6d349 100644 --- a/doc/coverage/coverage/index-sort-l.html +++ b/doc/coverage/coverage/index-sort-l.html @@ -31,27 +31,18 @@ lcov.info Lines: - 51 - 52 - 98.1 % + 125 + 131 + 95.4 % Date: - 2024-09-09 15:02:57 + 2026-08-17 11:57:58 Functions: - 12 - 14 - 85.7 % - - - - - - Branches: - 13 - 14 - 92.9 % + 36 + 40 + 90.0 % @@ -65,33 +56,38 @@ - - - - - - - - + + + + + + - + + + + + + + + - - - - - - + + + +


Directory Sort by name Line Coverage Sort by line coverage Functions Sort by function coverageBranches Sort by branch coverage
src -
98.1%98.1%
+
94.4%94.4%
+
94.4 %101 / 10787.5 %28 / 32
src/modules +
100.0%
98.1 %51 / 5285.7 %12 / 1492.9 %13 / 14100.0 %24 / 24100.0 %8 / 8
diff --git a/doc/coverage/coverage/index.html b/doc/coverage/coverage/index.html index 138d820..0278b86 100644 --- a/doc/coverage/coverage/index.html +++ b/doc/coverage/coverage/index.html @@ -31,27 +31,18 @@ lcov.info Lines: - 51 - 52 - 98.1 % + 125 + 131 + 95.4 % Date: - 2024-09-09 15:02:57 + 2026-08-17 11:57:58 Functions: - 12 - 14 - 85.7 % - - - - - - Branches: - 13 - 14 - 92.9 % + 36 + 40 + 90.0 % @@ -65,33 +56,38 @@ - - - - - - - - + + + + + + - + + + + + + + + - - - - - - + + + +


Directory Sort by name Line Coverage Sort by line coverage Functions Sort by function coverageBranches Sort by branch coverage
src -
98.1%98.1%
+
94.4%94.4%
+
94.4 %101 / 10787.5 %28 / 32
src/modules +
100.0%
98.1 %51 / 5285.7 %12 / 1492.9 %13 / 14100.0 %24 / 24100.0 %8 / 8
diff --git a/doc/coverage/coverage/src/DocumentEngine.sol.func-sort-c.html b/doc/coverage/coverage/src/DocumentEngine.sol.func-sort-c.html index 935fc26..d0559b5 100644 --- a/doc/coverage/coverage/src/DocumentEngine.sol.func-sort-c.html +++ b/doc/coverage/coverage/src/DocumentEngine.sol.func-sort-c.html @@ -31,28 +31,19 @@ lcov.info Lines: - 51 - 52 - 98.1 % + 17 + 19 + 89.5 % Date: - 2024-09-09 15:02:57 + 2026-08-17 11:57:58 Functions: - 12 - 14 + 6 + 7 85.7 % - - - - - Branches: - 13 - 14 - 92.9 % - @@ -69,60 +60,32 @@ Hit count Sort by hit count -
DocumentEngine._msgData +
DocumentEngine._msgData 0 - DocumentEngine.hasRole - 0 - - - DocumentEngine.removeDocument - 2 - - - DocumentEngine._removeDocument - 5 - - - DocumentEngine._removeDocumentName - 5 - - - DocumentEngine.batchRemoveDocuments - 7 - - - DocumentEngine.getAllDocuments + DocumentEngine.supportsInterface 8 - DocumentEngine.batchSetDocuments - 13 - - - DocumentEngine._getDocument - 20 - - - DocumentEngine.getDocument - 20 + DocumentEngine.constructor + 64 - DocumentEngine.setDocument - 28 + DocumentEngine.hasRole + 68 - DocumentEngine._setDocument - 39 + DocumentEngine._authorizeDocumentManagement + 882 - DocumentEngine._contextSuffixLength - 50 + DocumentEngine._contextSuffixLength + 966 - DocumentEngine._msgSender - 50 + DocumentEngine._msgSender + 966
diff --git a/doc/coverage/coverage/src/DocumentEngine.sol.func.html b/doc/coverage/coverage/src/DocumentEngine.sol.func.html index 4139274..5b6f1a0 100644 --- a/doc/coverage/coverage/src/DocumentEngine.sol.func.html +++ b/doc/coverage/coverage/src/DocumentEngine.sol.func.html @@ -31,28 +31,19 @@ lcov.info Lines: - 51 - 52 - 98.1 % + 17 + 19 + 89.5 % Date: - 2024-09-09 15:02:57 + 2026-08-17 11:57:58 Functions: - 12 - 14 + 6 + 7 85.7 % - - - - - Branches: - 13 - 14 - 92.9 % - @@ -69,61 +60,33 @@ Hit count Sort by hit count - DocumentEngine._contextSuffixLength - 50 + DocumentEngine._authorizeDocumentManagement + 882 - DocumentEngine._getDocument - 20 + DocumentEngine._contextSuffixLength + 966 - DocumentEngine._msgData + DocumentEngine._msgData 0 - DocumentEngine._msgSender - 50 - - - DocumentEngine._removeDocument - 5 - - - DocumentEngine._removeDocumentName - 5 - - - DocumentEngine._setDocument - 39 + DocumentEngine._msgSender + 966 - DocumentEngine.batchRemoveDocuments - 7 + DocumentEngine.constructor + 64 - DocumentEngine.batchSetDocuments - 13 + DocumentEngine.hasRole + 68 - DocumentEngine.getAllDocuments + DocumentEngine.supportsInterface 8 - - DocumentEngine.getDocument - 20 - - - DocumentEngine.hasRole - 0 - - - DocumentEngine.removeDocument - 2 - - - DocumentEngine.setDocument - 28 -
diff --git a/doc/coverage/coverage/src/DocumentEngine.sol.gcov.html b/doc/coverage/coverage/src/DocumentEngine.sol.gcov.html index f77a5a3..464d555 100644 --- a/doc/coverage/coverage/src/DocumentEngine.sol.gcov.html +++ b/doc/coverage/coverage/src/DocumentEngine.sol.gcov.html @@ -31,28 +31,19 @@ lcov.info Lines: - 51 - 52 - 98.1 % + 17 + 19 + 89.5 % Date: - 2024-09-09 15:02:57 + 2026-08-17 11:57:58 Functions: - 12 - 14 + 6 + 7 85.7 % - - - - - Branches: - 13 - 14 - 92.9 % - @@ -67,304 +58,174 @@ -

           Branch data     Line data    Source code
+
          Line data    Source code
-       1                 :            : //SPDX-License-Identifier: MPL-2.0
-       2                 :            : pragma solidity ^0.8.20;
-       3                 :            : 
-       4                 :            : import "OZ/access/AccessControl.sol";
-       5                 :            : import "OZ/metatx/ERC2771Context.sol";
-       6                 :            : import "CMTAT/interfaces/engine/draft-IERC1643.sol";
-       7                 :            : import "./DocumentEngineInvariant.sol";
-       8                 :            : 
-       9                 :            : /**
-      10                 :            :  * @title DocumentEngine
-      11                 :            :  * @notice contract to manage documents on-chain through ERC-1643
-      12                 :            :  */
-      13                 :            : contract DocumentEngine is
-      14                 :            :     IERC1643,
-      15                 :            :     DocumentEngineInvariant,
-      16                 :            :     AccessControl,
-      17                 :            :     ERC2771Context
-      18                 :            : {
-      19                 :            :     /**
-      20                 :            :      * @notice
-      21                 :            :      * Get the current version of the smart contract
-      22                 :            :      */
-      23                 :            :     string public constant VERSION = "0.3.0";
-      24                 :            :     // Mapping from contract addresses to document names to their corresponding Document structs
-      25                 :            :     mapping(address => mapping(bytes32 => Document)) private _documents;
-      26                 :            :     mapping(address => bytes32[]) private _documentNames;
-      27                 :            : 
-      28                 :            :     // Constructor to initialize the admin role
-      29                 :            :     constructor(
-      30                 :            :         address admin,
-      31                 :            :         address forwarderIrrevocable
-      32                 :            :     ) ERC2771Context(forwarderIrrevocable) {
-      33                 :            :         if (admin == address(0)) {
-      34                 :            :             revert AdminWithAddressZeroNotAllowed();
-      35                 :            :         }
-      36                 :            :         _grantRole(DEFAULT_ADMIN_ROLE, admin);
-      37                 :            :     }
-      38                 :            : 
-      39                 :            :     /*//////////////////////////////////////////////////////////////
-      40                 :            :                             PUBLIC/EXTERNAL FUNCTIONS
-      41                 :            :     //////////////////////////////////////////////////////////////*/
-      42                 :            : 
-      43                 :            :     /**
-      44                 :            :      * @notice Restricted function to set or update a document
-      45                 :            :      */
-      46                 :            :     function setDocument(
-      47                 :            :         address smartContract,
-      48                 :            :         bytes32 name_,
-      49                 :            :         string memory uri_,
-      50                 :            :         bytes32 documentHash_
-      51                 :            :     ) public onlyRole(DOCUMENT_MANAGER_ROLE) {
-      52                 :         54 :         _setDocument(smartContract, name_, uri_, documentHash_);
-      53                 :            :     }
-      54                 :            : 
-      55                 :            :     /**
-      56                 :            :      * @notice Restricted function to remove a document for a given smart contract and name
-      57                 :            :      */
-      58                 :            :     function removeDocument(
-      59                 :            :         address smartContract,
-      60                 :            :         bytes32 name_
-      61                 :            :     ) external onlyRole(DOCUMENT_MANAGER_ROLE) {
-      62                 :          2 :         _removeDocument(smartContract, name_);
-      63                 :            :     }
-      64                 :            : 
-      65                 :            :     /**
-      66                 :            :      * @notice Batch version of setDocument to handle multiple documents at once
-      67                 :            :      */
-      68                 :            :     function batchSetDocuments(
-      69                 :            :         address[] calldata smartContracts,
-      70                 :            :         bytes32[] calldata names,
-      71                 :            :         string[] calldata uris,
-      72                 :            :         bytes32[] calldata hashes
-      73                 :            :     ) external onlyRole(DOCUMENT_MANAGER_ROLE) {
-      74         [ +  + ]:            :         if (
-      75                 :         35 :             smartContracts.length == 0 ||
-      76                 :         12 :             smartContracts.length != names.length ||
-      77                 :         10 :             names.length != uris.length ||
-      78                 :          8 :             uris.length != hashes.length
-      79                 :            :         ) {
-      80                 :          6 :             revert InvalidInputLength();
-      81                 :            :         }
-      82                 :         28 :         for (uint256 i = 0; i < smartContracts.length; i++) {
-      83                 :         16 :             _setDocument(smartContracts[i], names[i], uris[i], hashes[i]);
-      84                 :            :         }
-      85                 :            :     }
-      86                 :            : 
-      87                 :            :     /**
-      88                 :            :      * @notice Batch version of setDocument to handle multiple documents at once
-      89                 :            :      */
-      90                 :            :     function batchSetDocuments(
-      91                 :            :         address smartContract,
-      92                 :            :         bytes32[] calldata names,
-      93                 :            :         string[] calldata uris,
-      94                 :            :         bytes32[] calldata hashes
-      95                 :            :     ) external onlyRole(DOCUMENT_MANAGER_ROLE) {
-      96         [ +  + ]:            :         if (
-      97                 :         16 :             names.length == 0 ||
-      98                 :          6 :             names.length != uris.length ||
-      99                 :          4 :             uris.length != hashes.length
-     100                 :            :         ) {
-     101                 :          4 :             revert InvalidInputLength();
-     102                 :            :         }
-     103                 :         14 :         for (uint256 i = 0; i < names.length; ++i) {
-     104                 :          8 :             _setDocument(smartContract, names[i], uris[i], hashes[i]);
-     105                 :            :         }
-     106                 :            :     }
-     107                 :            : 
-     108                 :            :     /**
-     109                 :            :      * @notice Batch version of removeDocument to handle multiple documents at once
-     110                 :            :      */
-     111                 :            :     function batchRemoveDocuments(
-     112                 :            :         address[] calldata smartContracts,
-     113                 :            :         bytes32[] calldata names
-     114                 :            :     ) external onlyRole(DOCUMENT_MANAGER_ROLE) {
-     115         [ +  + ]:            :         if (
-     116                 :          9 :             smartContracts.length == 0 ||
-     117                 :            :             (smartContracts.length != names.length)
-     118                 :            :         ) {
-     119                 :          4 :             revert InvalidInputLength();
-     120                 :            :         }
-     121                 :            : 
-     122                 :          7 :         for (uint256 i = 0; i < smartContracts.length; ++i) {
-     123                 :          4 :             _removeDocument(smartContracts[i], names[i]);
-     124                 :            :         }
-     125                 :            :     }
-     126                 :            : 
-     127                 :            :     /**
-     128                 :            :      * @notice Batch version of removeDocument to handle multiple documents at once
-     129                 :            :      */
-     130                 :            :     function batchRemoveDocuments(
-     131                 :            :         address smartContract,
-     132                 :            :         bytes32[] calldata names
-     133                 :            :     ) external onlyRole(DOCUMENT_MANAGER_ROLE) {
-     134         [ +  + ]:          4 :         if (names.length == 0) {
-     135                 :          2 :             revert InvalidInputLength();
-     136                 :            :         }
-     137                 :            : 
-     138                 :          7 :         for (uint256 i = 0; i < names.length; ++i) {
-     139                 :          4 :             _removeDocument(smartContract, names[i]);
-     140                 :            :         }
-     141                 :            :     }
-     142                 :            : 
-     143                 :            :     /**
-     144                 :            :      * @notice Public function to get a document from msg.sender
-     145                 :            :      */
-     146                 :            :     function getDocument(
-     147                 :            :         bytes32 name_
-     148                 :            :     ) external view override returns (string memory, bytes32, uint256) {
-     149                 :          3 :         return _getDocument(msg.sender, name_);
-     150                 :            :     }
-     151                 :            : 
-     152                 :            :     /**
-     153                 :            :      * @notice Public function to get a document for a specific contract address
-     154                 :            :      */
-     155                 :            :     function getDocument(
-     156                 :            :         address smartContract,
-     157                 :            :         bytes32 name_
-     158                 :            :     ) external view returns (string memory, bytes32, uint256) {
-     159                 :         57 :         return _getDocument(smartContract, name_);
-     160                 :            :     }
-     161                 :            : 
-     162                 :            :     /**
-     163                 :            :      * @notice Get all document names for msg.sender
-     164                 :            :      */
-     165                 :            :     function getAllDocuments()
-     166                 :            :         external
-     167                 :            :         view
-     168                 :            :         override
-     169                 :            :         returns (bytes32[] memory)
-     170                 :            :     {
-     171                 :          2 :         return _documentNames[msg.sender];
-     172                 :            :     }
-     173                 :            : 
-     174                 :            :     /**
-     175                 :            :      * @notice Get all document names for a specific smart contract
-     176                 :            :      */
-     177                 :            :     function getAllDocuments(
-     178                 :            :         address smartContract
-     179                 :            :     ) external view returns (bytes32[] memory) {
-     180                 :         14 :         return _documentNames[smartContract];
-     181                 :            :     }
-     182                 :            : 
-     183                 :            :     /* ============ ACCESS CONTROL ============ */
-     184                 :            :     /*
-     185                 :            :      * @dev Returns `true` if `account` has been granted `role`.
-     186                 :            :      */
-     187                 :            :     function hasRole(
-     188                 :            :         bytes32 role,
-     189                 :            :         address account
-     190                 :            :     ) public view virtual override returns (bool) {
-     191                 :            :         // The Default Admin has all roles
-     192         [ +  + ]:        100 :         if (AccessControl.hasRole(DEFAULT_ADMIN_ROLE, account)) {
-     193                 :         88 :             return true;
-     194                 :            :         }
-     195                 :         18 :         return AccessControl.hasRole(role, account);
-     196                 :            :     }
-     197                 :            : 
-     198                 :            :     /*//////////////////////////////////////////////////////////////
-     199                 :            :                             INTERNAL FUNCTIONS
-     200                 :            :     //////////////////////////////////////////////////////////////*/
-     201                 :            : 
-     202                 :            :     /**
-     203                 :            :      * @dev Internal function to fetch a document
-     204                 :            :      */
-     205                 :            :     function _getDocument(
-     206                 :            :         address smartContract,
-     207                 :            :         bytes32 name_
-     208                 :            :     ) internal view returns (string memory, bytes32, uint256) {
-     209                 :         40 :         Document memory doc = _documents[smartContract][name_];
-     210                 :         40 :         return (doc.uri, doc.documentHash, doc.lastModified);
-     211                 :            :     }
-     212                 :            : 
-     213                 :            :     /**
-     214                 :            :      * @dev Internal helper to remove the document name from the list of document names
-     215                 :            :      */
-     216                 :            :     function _removeDocumentName(
-     217                 :            :         address smartContract,
-     218                 :            :         bytes32 name_
-     219                 :            :     ) internal {
-     220                 :         10 :         uint256 length = _documentNames[smartContract].length;
-     221                 :         15 :         for (uint256 i = 0; i < length; ++i) {
-     222         [ #  + ]:         10 :             if (_documentNames[smartContract][i] == name_) {
-     223                 :         10 :                 _documentNames[smartContract][i] = _documentNames[
-     224                 :            :                     smartContract
-     225                 :            :                 ][length - 1];
-     226                 :         10 :                 _documentNames[smartContract].pop();
-     227                 :         10 :                 break;
-     228                 :            :             }
-     229                 :            :         }
-     230                 :            :     }
-     231                 :            : 
-     232                 :            :     function _removeDocument(address smartContract, bytes32 name_) internal {
-     233                 :         10 :         Document memory doc = _documents[smartContract][name_];
-     234                 :         10 :         emit DocumentRemoved(smartContract, name_, doc.uri, doc.documentHash);
-     235                 :            : 
-     236                 :         10 :         delete _documents[smartContract][name_];
-     237                 :         10 :         _removeDocumentName(smartContract, name_);
-     238                 :            :     }
-     239                 :            : 
-     240                 :            :     function _setDocument(
-     241                 :            :         address smartContract,
-     242                 :            :         bytes32 name_,
-     243                 :            :         string memory uri_,
-     244                 :            :         bytes32 documentHash_
-     245                 :            :     ) internal {
-     246                 :         78 :         Document storage doc = _documents[smartContract][name_];
-     247         [ +  + ]:         78 :         if (doc.lastModified == 0) {
-     248                 :            :             // new document
-     249                 :         60 :             _documentNames[smartContract].push(name_);
-     250                 :            :         }
-     251                 :         78 :         doc.uri = uri_;
-     252                 :         78 :         doc.documentHash = documentHash_;
-     253                 :         78 :         doc.lastModified = block.timestamp;
-     254                 :         78 :         emit DocumentUpdated(smartContract, name_, uri_, documentHash_);
-     255                 :            :     }
-     256                 :            : 
-     257                 :            :     /*//////////////////////////////////////////////////////////////
-     258                 :            :                            ERC2771
-     259                 :            :     //////////////////////////////////////////////////////////////*/
-     260                 :            : 
-     261                 :            :     /**
-     262                 :            :      * @dev This surcharge is not necessary if you do not use ERC2771
-     263                 :            :      */
-     264                 :            :     function _msgSender()
-     265                 :            :         internal
-     266                 :            :         view
-     267                 :            :         override(ERC2771Context, Context)
-     268                 :            :         returns (address sender)
-     269                 :            :     {
-     270                 :        150 :         return ERC2771Context._msgSender();
-     271                 :            :     }
-     272                 :            : 
-     273                 :            :     /**
-     274                 :            :      * @dev This surcharge is not necessary if you do not use ERC2771
-     275                 :            :      */
-     276                 :            :     function _msgData()
-     277                 :            :         internal
-     278                 :            :         view
-     279                 :            :         override(ERC2771Context, Context)
-     280                 :            :         returns (bytes calldata)
-     281                 :            :     {
-     282                 :          0 :         return ERC2771Context._msgData();
-     283                 :            :     }
-     284                 :            : 
-     285                 :            :     /**
-     286                 :            :      * @dev This surcharge is not necessary if you do not use the MetaTxModule
-     287                 :            :      */
-     288                 :            :     function _contextSuffixLength()
-     289                 :            :         internal
-     290                 :            :         view
-     291                 :            :         override(ERC2771Context, Context)
-     292                 :            :         returns (uint256)
-     293                 :            :     {
-     294                 :        150 :         return ERC2771Context._contextSuffixLength();
-     295                 :            :     }
-     296                 :            : }
+       1             : //SPDX-License-Identifier: MPL-2.0
+       2             : pragma solidity ^0.8.24;
+       3             : 
+       4             : import {AccessControl} from "OZ/access/AccessControl.sol";
+       5             : import {AccessControlEnumerable} from "OZ/access/extensions/AccessControlEnumerable.sol";
+       6             : import {IAccessControl} from "OZ/access/IAccessControl.sol";
+       7             : import {Context} from "OZ/utils/Context.sol";
+       8             : import {ERC2771Context} from "OZ/metatx/ERC2771Context.sol";
+       9             : import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol";
+      10             : import {IERC1643MultiDocument} from "./interfaces/IERC1643MultiDocument.sol";
+      11             : import {ITokenBinding} from "./interfaces/ITokenBinding.sol";
+      12             : import {TokenBindingModule} from "./modules/TokenBindingModule.sol";
+      13             : import {VersionModule} from "./modules/VersionModule.sol";
+      14             : 
+      15             : /**
+      16             :  * @title DocumentEngine
+      17             :  * @notice Deployment contract to manage documents on-chain through ERC-1643.
+      18             :  * @dev Wires the document-management logic ({DocumentEngineBase}) with a
+      19             :  * concrete access-control implementation. The authorization hooks are defined
+      20             :  * here (role-based `AccessControlEnumerable`, which additionally allows
+      21             :  * enumerating role members), keeping the access control separate from the
+      22             :  * document-management logic (CMTAT / CMTA-RuleEngine pattern). The contract
+      23             :  * version is exposed through the {VersionModule} (ERC-8303), and it also wires
+      24             :  * the ERC-2771 (gasless) meta-transaction support.
+      25             :  */
+      26             : contract DocumentEngine is TokenBindingModule, VersionModule, AccessControlEnumerable, ERC2771Context {
+      27             :     /**
+      28             :      * @notice Role allowed to manage documents on behalf of any smart contract, and to
+      29             :      * bind/unbind tokens (admin path).
+      30             :      * @dev Token binding uses the shared allowlist in {TokenBindingModule}, not a dedicated role.
+      31             :      */
+      32             :     bytes32 public constant DOCUMENT_MANAGER_ROLE = keccak256("DOCUMENT_MANAGER_ROLE");
+      33             : 
+      34             :     /**
+      35             :      * @notice Deploys the engine and grants `admin` the default admin role.
+      36             :      * @param admin address granted `DEFAULT_ADMIN_ROLE`; must not be the null address
+      37             :      * @param forwarderIrrevocable address of the ERC-2771 forwarder (gasless support)
+      38             :      */
+      39          64 :     constructor(address admin, address forwarderIrrevocable) ERC2771Context(forwarderIrrevocable) {
+      40          64 :         if (admin == address(0)) {
+      41           1 :             revert AdminWithAddressZeroNotAllowed();
+      42             :         }
+      43          63 :         _grantRole(DEFAULT_ADMIN_ROLE, admin);
+      44             :     }
+      45             : 
+      46             :     /*//////////////////////////////////////////////////////////////
+      47             :                         ACCESS CONTROL (public surface)
+      48             :     //////////////////////////////////////////////////////////////*/
+      49             : 
+      50             :     /**
+      51             :      * @notice Returns whether `account` holds `role`.
+      52             :      * @dev Returns `true` if `account` has been granted `role`. The default admin
+      53             :      * (`DEFAULT_ADMIN_ROLE`) is treated as holding **every** role.
+      54             :      *
+      55             :      * Note: this virtual "admin has all roles" behavior is NOT reflected by
+      56             :      * {AccessControlEnumerable} enumeration. `getRoleMember` / `getRoleMemberCount`
+      57             :      * report only explicit grants, so a `DEFAULT_ADMIN_ROLE` holder satisfies
+      58             :      * `hasRole(anyRole, admin)` yet does not appear in `getRoleMember(anyRole, ...)`.
+      59             :      *
+      60             :      * WARNING: the same short-circuit makes a role **unrevokable from the default admin**.
+      61             :      * `revokeRole(someRole, admin)` succeeds and emits `RoleRevoked` — the explicit grant is
+      62             :      * genuinely removed, and `getRoleMemberCount` drops — but this function still answers
+      63             :      * `true`, so the admin keeps the access the caller believed it had just removed. Only
+      64             :      * revoking `DEFAULT_ADMIN_ROLE` itself actually withdraws it. This is inherent to the
+      65             :      * "admin has all roles" model rather than a defect (an admin can always re-grant itself
+      66             :      * any role), but the success of the call is misleading. Pinned by
+      67             :      * `testRevokingRoleFromDefaultAdminDoesNotRemoveAccess`.
+      68             :      * @param role The role identifier to check.
+      69             :      * @param account The account to check.
+      70             :      * @return True when `account` holds `role`, or holds `DEFAULT_ADMIN_ROLE`.
+      71             :      */
+      72          68 :     function hasRole(bytes32 role, address account)
+      73             :         public
+      74             :         view
+      75             :         virtual
+      76             :         override(AccessControl, IAccessControl)
+      77             :         returns (bool)
+      78             :     {
+      79             :         // The Default Admin has all roles
+      80         952 :         if (super.hasRole(DEFAULT_ADMIN_ROLE, account)) {
+      81         882 :             return true;
+      82             :         }
+      83          70 :         return super.hasRole(role, account);
+      84             :     }
+      85             : 
+      86             :     /*//////////////////////////////////////////////////////////////
+      87             :                            ERC165
+      88             :     //////////////////////////////////////////////////////////////*/
+      89             : 
+      90             :     /**
+      91             :      * @notice Returns whether this contract implements `interfaceId`.
+      92             :      * @dev ERC-165 discovery: advertises ERC-1643 and its multi-subject extension, the token-binding
+      93             :      * surface, the version module (ERC-8303) and `AccessControlEnumerable`.
+      94             :      *
+      95             :      * `type(IERC1643).interfaceId` is advertised because the engine does implement the base
+      96             :      * single-argument functions, which is exactly what the draft conditions the id on. Its audience
+      97             :      * is a **token wiring itself to this engine**: before calling `setDocumentEngine(engine)`, or
+      98             :      * before forwarding `setDocument(name, uri, hash)` to it, a token can confirm through ERC-165
+      99             :      * that the single-argument ERC-1643 endpoints exist here, rather than finding out from a failed
+     100             :      * call. `type(ITokenBinding).interfaceId` answers the complementary question — whether this
+     101             :      * engine has a binding surface at all — and `isTokenBound(address(this))` whether that
+     102             :      * particular token may use it.
+     103             :      *
+     104             :      * It is **not** an invitation to read documents from this address. The base functions are
+     105             :      * `_msgSender()`-scoped, so a consumer calling `getDocument(name)` here reads its own, empty
+     106             :      * namespace, and this engine emits only the address-carrying `*ForSubject` events. Point
+     107             :      * document consumers at the **subject**, or use the address-scoped `getDocument(subject, name)`.
+     108             :      *
+     109             :      * See {IERC165-supportsInterface}.
+     110             :      * @param interfaceId The ERC-165 interface identifier to query.
+     111             :      * @return True when `interfaceId` is one of the advertised interfaces or is supported by a base
+     112             :      * contract.
+     113             :      */
+     114           8 :     function supportsInterface(bytes4 interfaceId)
+     115             :         public
+     116             :         view
+     117             :         virtual
+     118             :         override(VersionModule, AccessControlEnumerable)
+     119             :         returns (bool)
+     120             :     {
+     121           8 :         return interfaceId == type(IERC1643).interfaceId || interfaceId == type(IERC1643MultiDocument).interfaceId
+     122           5 :             || interfaceId == type(ITokenBinding).interfaceId || super.supportsInterface(interfaceId);
+     123             :     }
+     124             : 
+     125             :     /*//////////////////////////////////////////////////////////////
+     126             :                         ACCESS CONTROL (implementation)
+     127             :     //////////////////////////////////////////////////////////////*/
+     128             : 
+     129             :     /**
+     130             :      * @dev Authorization for the admin document-management path.
+     131             :      * The caller must hold `DOCUMENT_MANAGER_ROLE`. Override to customize.
+     132             :      */
+     133         882 :     function _authorizeDocumentManagement() internal view virtual override {
+     134         882 :         _checkRole(DOCUMENT_MANAGER_ROLE);
+     135             :     }
+     136             : 
+     137             :     /*//////////////////////////////////////////////////////////////
+     138             :                            ERC2771
+     139             :     //////////////////////////////////////////////////////////////*/
+     140             : 
+     141             :     /**
+     142             :      * @dev This surcharge is not necessary if you do not use ERC2771
+     143             :      * @return sender The transaction sender, unwrapped from the ERC-2771 calldata suffix when the
+     144             :      * call came through the trusted forwarder.
+     145             :      */
+     146         966 :     function _msgSender() internal view virtual override(ERC2771Context, Context) returns (address sender) {
+     147         966 :         return ERC2771Context._msgSender();
+     148             :     }
+     149             : 
+     150             :     /**
+     151             :      * @dev This surcharge is not necessary if you do not use ERC2771
+     152             :      * @return The calldata, stripped of the ERC-2771 sender suffix when the call came through the
+     153             :      * trusted forwarder.
+     154             :      */
+     155           0 :     function _msgData() internal view virtual override(ERC2771Context, Context) returns (bytes calldata) {
+     156           0 :         return ERC2771Context._msgData();
+     157             :     }
+     158             : 
+     159             :     /**
+     160             :      * @dev This surcharge is not necessary if you do not use the MetaTxModule
+     161             :      * @return The length of the ERC-2771 calldata suffix holding the sender address.
+     162             :      */
+     163         966 :     function _contextSuffixLength() internal view virtual override(ERC2771Context, Context) returns (uint256) {
+     164         966 :         return ERC2771Context._contextSuffixLength();
+     165             :     }
+     166             : }
 
diff --git a/doc/coverage/coverage/src/DocumentEngineBase.sol.func-sort-c.html b/doc/coverage/coverage/src/DocumentEngineBase.sol.func-sort-c.html new file mode 100644 index 0000000..b661e5a --- /dev/null +++ b/doc/coverage/coverage/src/DocumentEngineBase.sol.func-sort-c.html @@ -0,0 +1,152 @@ + + + + + + + LCOV - lcov.info - src/DocumentEngineBase.sol - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - src - DocumentEngineBase.sol (source / functions)HitTotalCoverage
Test:lcov.infoLines:757797.4 %
Date:2026-08-17 11:57:58Functions:182090.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
DocumentEngineBase._authorizeBoundTokenDocumentManagement0
DocumentEngineBase._authorizeDocumentManagement0
DocumentEngineBase.batchRemoveDocuments.13
DocumentEngineBase.getAllDocuments.03
DocumentEngineBase.onlyDocumentManager3
DocumentEngineBase.removeDocument.13
DocumentEngineBase.batchRemoveDocuments.05
DocumentEngineBase.batchSetDocuments.15
DocumentEngineBase.getDocument.05
DocumentEngineBase.onlyBoundToken7
DocumentEngineBase.setDocument.07
DocumentEngineBase.batchSetDocuments.010
DocumentEngineBase.removeDocument.0263
DocumentEngineBase.getAllDocuments.1266
DocumentEngineBase._removeDocumentName267
DocumentEngineBase._removeDocument269
DocumentEngineBase.setDocument.1587
DocumentEngineBase._setDocument603
DocumentEngineBase.getDocument.1798
DocumentEngineBase._getDocument803
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/doc/coverage/coverage/src/DocumentEngineBase.sol.func.html b/doc/coverage/coverage/src/DocumentEngineBase.sol.func.html new file mode 100644 index 0000000..d81a090 --- /dev/null +++ b/doc/coverage/coverage/src/DocumentEngineBase.sol.func.html @@ -0,0 +1,152 @@ + + + + + + + LCOV - lcov.info - src/DocumentEngineBase.sol - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - src - DocumentEngineBase.sol (source / functions)HitTotalCoverage
Test:lcov.infoLines:757797.4 %
Date:2026-08-17 11:57:58Functions:182090.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
DocumentEngineBase._authorizeBoundTokenDocumentManagement0
DocumentEngineBase._authorizeDocumentManagement0
DocumentEngineBase._getDocument803
DocumentEngineBase._removeDocument269
DocumentEngineBase._removeDocumentName267
DocumentEngineBase._setDocument603
DocumentEngineBase.batchRemoveDocuments.05
DocumentEngineBase.batchRemoveDocuments.13
DocumentEngineBase.batchSetDocuments.010
DocumentEngineBase.batchSetDocuments.15
DocumentEngineBase.getAllDocuments.03
DocumentEngineBase.getAllDocuments.1266
DocumentEngineBase.getDocument.05
DocumentEngineBase.getDocument.1798
DocumentEngineBase.onlyBoundToken7
DocumentEngineBase.onlyDocumentManager3
DocumentEngineBase.removeDocument.0263
DocumentEngineBase.removeDocument.13
DocumentEngineBase.setDocument.07
DocumentEngineBase.setDocument.1587
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/doc/coverage/coverage/src/DocumentEngineBase.sol.gcov.html b/doc/coverage/coverage/src/DocumentEngineBase.sol.gcov.html new file mode 100644 index 0000000..99391b7 --- /dev/null +++ b/doc/coverage/coverage/src/DocumentEngineBase.sol.gcov.html @@ -0,0 +1,439 @@ + + + + + + + LCOV - lcov.info - src/DocumentEngineBase.sol + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - src - DocumentEngineBase.sol (source / functions)HitTotalCoverage
Test:lcov.infoLines:757797.4 %
Date:2026-08-17 11:57:58Functions:182090.0 %
+
+ + + + + + + + +

+
          Line data    Source code
+
+       1             : //SPDX-License-Identifier: MPL-2.0
+       2             : pragma solidity ^0.8.24;
+       3             : 
+       4             : import {Context} from "OZ/utils/Context.sol";
+       5             : import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol";
+       6             : import {IERC1643MultiDocument} from "./interfaces/IERC1643MultiDocument.sol";
+       7             : import {DocumentEngineInvariant} from "./DocumentEngineInvariant.sol";
+       8             : 
+       9             : /**
+      10             :  * @title DocumentEngineBase
+      11             :  * @notice Document management logic (ERC-1643) for several smart contracts.
+      12             :  * @dev This abstract base holds the document storage and all the
+      13             :  * document-management functions, but it is **agnostic to the access-control
+      14             :  * implementation**. Authorization is delegated to the abstract hooks
+      15             :  * {_authorizeDocumentManagement} and {_authorizeBoundTokenDocumentManagement}
+      16             :  * (through the `onlyDocumentManager` / `onlyBoundToken` modifiers), which a
+      17             :  * deployment contract must implement (see {DocumentEngine}).
+      18             :  *
+      19             :  * This separation (base logic + deployment-defined access control) follows the
+      20             :  * CMTAT and CMTA/RuleEngine pattern.
+      21             :  */
+      22             : abstract contract DocumentEngineBase is IERC1643, IERC1643MultiDocument, DocumentEngineInvariant, Context {
+      23             :     /**
+      24             :      * @notice Documents held for each subject, keyed by subject address then document name.
+      25             :      */
+      26             :     mapping(address => mapping(bytes32 => Document)) private _documents;
+      27             : 
+      28             :     /**
+      29             :      * @notice The names of every document currently tracked for each subject.
+      30             :      */
+      31             :     mapping(address => bytes32[]) private _documentNames;
+      32             : 
+      33             :     /*//////////////////////////////////////////////////////////////
+      34             :                         ACCESS CONTROL (modifiers)
+      35             :     //////////////////////////////////////////////////////////////*/
+      36             : 
+      37             :     /**
+      38             :      * @dev Restricts a function to accounts allowed to manage documents on
+      39             :      * behalf of any smart contract (admin path). Delegates the authorization
+      40             :      * to {_authorizeDocumentManagement} so that the document-management
+      41             :      * implementation stays separate from the access-control logic.
+      42             :      */
+      43           3 :     modifier onlyDocumentManager() {
+      44           3 :         _authorizeDocumentManagement();
+      45             :         _;
+      46             :     }
+      47             : 
+      48             :     /**
+      49             :      * @dev Restricts a function to tokens bound to this engine, letting them
+      50             :      * manage their own documents (bound-token path). Delegates to
+      51             :      * {_authorizeBoundTokenDocumentManagement}.
+      52             :      */
+      53           7 :     modifier onlyBoundToken() {
+      54           7 :         _authorizeBoundTokenDocumentManagement();
+      55             :         _;
+      56             :     }
+      57             : 
+      58             :     /*//////////////////////////////////////////////////////////////
+      59             :                             EXTERNAL FUNCTIONS
+      60             :     //////////////////////////////////////////////////////////////*/
+      61             : 
+      62             :     /**
+      63             :      * @notice Restricted function to remove a document for a given smart contract and name
+      64             :      * @param subject The contract the document belongs to.
+      65             :      * @param name_ The document name.
+      66             :      */
+      67         263 :     function removeDocument(address subject, bytes32 name_) external override onlyDocumentManager {
+      68         262 :         _removeDocument(subject, name_);
+      69             :     }
+      70             : 
+      71             :     /* ============ ERC-1643 (bound token) ============ */
+      72             : 
+      73             :     /**
+      74             :      * @notice ERC-1643 function to set or update a document for the caller.
+      75             :      * @dev The document is stored under the caller (`_msgSender()`) namespace.
+      76             :      * Restricted by the `onlyBoundToken` hook: the caller must be a token bound to
+      77             :      * this engine. How a token is bound is deployment-specific (see the
+      78             :      * {_authorizeBoundTokenDocumentManagement} implementations). A bound token can
+      79             :      * only manage its own documents; it can never affect another contract's documents.
+      80             :      * @param name_ The document name.
+      81             :      * @param uri_ The document location.
+      82             :      * @param documentHash_ The hash of the document contents.
+      83             :      */
+      84           7 :     function setDocument(bytes32 name_, string calldata uri_, bytes32 documentHash_) external override onlyBoundToken {
+      85           4 :         _setDocument(_msgSender(), name_, uri_, documentHash_);
+      86             :     }
+      87             : 
+      88             :     /**
+      89             :      * @notice ERC-1643 function to remove a document for the caller.
+      90             :      * @dev See {setDocument}. Scoped to the caller (`_msgSender()`) namespace.
+      91             :      * @param name_ The document name.
+      92             :      */
+      93           3 :     function removeDocument(bytes32 name_) external override onlyBoundToken {
+      94           2 :         _removeDocument(_msgSender(), name_);
+      95             :     }
+      96             : 
+      97             :     /**
+      98             :      * @notice Batch version of setDocument to handle multiple documents at once
+      99             :      * @dev All-or-nothing: a single invalid entry reverts the whole batch.
+     100             :      * @param subjects The contract each document belongs to, one per entry.
+     101             :      * @param names The document names, one per entry.
+     102             :      * @param uris The document locations, one per entry.
+     103             :      * @param hashes The document content hashes, one per entry.
+     104             :      */
+     105          10 :     function batchSetDocuments(
+     106             :         address[] calldata subjects,
+     107             :         bytes32[] calldata names,
+     108             :         string[] calldata uris,
+     109             :         bytes32[] calldata hashes
+     110             :     ) external onlyDocumentManager {
+     111             :         if (
+     112           9 :             subjects.length == 0 || subjects.length != names.length || names.length != uris.length
+     113           6 :                 || uris.length != hashes.length
+     114           3 :         ) {
+     115           3 :             revert InvalidInputLength();
+     116             :         }
+     117           6 :         uint256 length = subjects.length;
+     118           6 :         for (uint256 i = 0; i < length; ++i) {
+     119          10 :             _setDocument(subjects[i], names[i], uris[i], hashes[i]);
+     120             :         }
+     121             :     }
+     122             : 
+     123             :     /**
+     124             :      * @notice Batch version of setDocument to handle multiple documents at once
+     125             :      * @dev All-or-nothing: a single invalid entry reverts the whole batch.
+     126             :      * @param subject The contract every document in the batch belongs to.
+     127             :      * @param names The document names, one per entry.
+     128             :      * @param uris The document locations, one per entry.
+     129             :      * @param hashes The document content hashes, one per entry.
+     130             :      */
+     131           5 :     function batchSetDocuments(
+     132             :         address subject,
+     133             :         bytes32[] calldata names,
+     134             :         string[] calldata uris,
+     135             :         bytes32[] calldata hashes
+     136             :     ) external onlyDocumentManager {
+     137           4 :         if (names.length == 0 || names.length != uris.length || uris.length != hashes.length) {
+     138           2 :             revert InvalidInputLength();
+     139             :         }
+     140           2 :         uint256 length = names.length;
+     141           2 :         for (uint256 i = 0; i < length; ++i) {
+     142           4 :             _setDocument(subject, names[i], uris[i], hashes[i]);
+     143             :         }
+     144             :     }
+     145             : 
+     146             :     /**
+     147             :      * @notice Batch version of removeDocument to handle multiple documents at once
+     148             :      * @dev All-or-nothing: a single missing document reverts the whole batch.
+     149             :      * @param subjects The contract each document belongs to, one per entry.
+     150             :      * @param names The document names, one per entry.
+     151             :      */
+     152           5 :     function batchRemoveDocuments(address[] calldata subjects, bytes32[] calldata names) external onlyDocumentManager {
+     153           4 :         if (subjects.length == 0 || (subjects.length != names.length)) {
+     154           2 :             revert InvalidInputLength();
+     155             :         }
+     156             : 
+     157           2 :         uint256 length = subjects.length;
+     158           2 :         for (uint256 i = 0; i < length; ++i) {
+     159           3 :             _removeDocument(subjects[i], names[i]);
+     160             :         }
+     161             :     }
+     162             : 
+     163             :     /**
+     164             :      * @notice Batch version of removeDocument to handle multiple documents at once
+     165             :      * @dev All-or-nothing: a single missing document reverts the whole batch.
+     166             :      * @param subject The contract every document in the batch belongs to.
+     167             :      * @param names The document names, one per entry.
+     168             :      */
+     169           3 :     function batchRemoveDocuments(address subject, bytes32[] calldata names) external onlyDocumentManager {
+     170           2 :         if (names.length == 0) {
+     171           1 :             revert InvalidInputLength();
+     172             :         }
+     173             : 
+     174           1 :         uint256 length = names.length;
+     175           1 :         for (uint256 i = 0; i < length; ++i) {
+     176           2 :             _removeDocument(subject, names[i]);
+     177             :         }
+     178             :     }
+     179             : 
+     180             :     /**
+     181             :      * @notice ERC-1643 function to get a document for the caller (`_msgSender()`)
+     182             :      * @dev Returns the three fields as flat values, matching the ERC-1643 ABI. The `Document`
+     183             :      * struct is kept for storage only: returning it would prepend a struct offset word to the
+     184             :      * returndata, so a consumer decoding per the ERC-1643 signature would silently mis-decode.
+     185             :      * @param name_ The document name.
+     186             :      * @return uri Document location.
+     187             :      * @return documentHash Hash of the document contents.
+     188             :      * @return lastModified Last update timestamp.
+     189             :      */
+     190           5 :     function getDocument(bytes32 name_)
+     191             :         external
+     192             :         view
+     193             :         override
+     194             :         returns (string memory uri, bytes32 documentHash, uint256 lastModified)
+     195             :     {
+     196           5 :         return _getDocument(_msgSender(), name_);
+     197             :     }
+     198             : 
+     199             :     /**
+     200             :      * @notice Public function to get a document for a specific contract address
+     201             :      * @dev Flat return, see {getDocument(bytes32)}.
+     202             :      * @param subject The contract the document belongs to.
+     203             :      * @param name_ The document name.
+     204             :      * @return uri Document location.
+     205             :      * @return documentHash Hash of the document contents.
+     206             :      * @return lastModified Last update timestamp.
+     207             :      */
+     208         798 :     function getDocument(address subject, bytes32 name_)
+     209             :         external
+     210             :         view
+     211             :         override
+     212             :         returns (string memory uri, bytes32 documentHash, uint256 lastModified)
+     213             :     {
+     214         798 :         return _getDocument(subject, name_);
+     215             :     }
+     216             : 
+     217             :     /**
+     218             :      * @notice Get all document names for msg.sender
+     219             :      * @return The names of every document currently tracked for the caller.
+     220             :      */
+     221           3 :     function getAllDocuments() external view override returns (bytes32[] memory) {
+     222           3 :         return _documentNames[_msgSender()];
+     223             :     }
+     224             : 
+     225             :     /**
+     226             :      * @notice Get all document names for a specific smart contract
+     227             :      * @param subject The contract to enumerate documents for.
+     228             :      * @return The names of every document currently tracked for `subject`.
+     229             :      */
+     230         266 :     function getAllDocuments(address subject) external view override returns (bytes32[] memory) {
+     231         266 :         return _documentNames[subject];
+     232             :     }
+     233             : 
+     234             :     /*//////////////////////////////////////////////////////////////
+     235             :                             PUBLIC FUNCTIONS
+     236             :     //////////////////////////////////////////////////////////////*/
+     237             : 
+     238             :     /**
+     239             :      * @notice Restricted function to set or update a document
+     240             :      * @param subject The contract the document belongs to.
+     241             :      * @param name_ The document name.
+     242             :      * @param uri_ The document location.
+     243             :      * @param documentHash_ The hash of the document contents.
+     244             :      */
+     245         587 :     function setDocument(address subject, bytes32 name_, string memory uri_, bytes32 documentHash_)
+     246             :         public
+     247             :         override
+     248             :         onlyDocumentManager
+     249             :     {
+     250         585 :         _setDocument(subject, name_, uri_, documentHash_);
+     251             :     }
+     252             : 
+     253             :     /*//////////////////////////////////////////////////////////////
+     254             :                             INTERNAL FUNCTIONS
+     255             :     //////////////////////////////////////////////////////////////*/
+     256             : 
+     257             :     /**
+     258             :      * @dev Internal helper to remove the document name from the list of document names
+     259             :      * @param subject The contract the document belongs to.
+     260             :      * @param name_ The document name to remove from the list.
+     261             :      */
+     262         267 :     function _removeDocumentName(address subject, bytes32 name_) internal virtual {
+     263         267 :         bytes32[] storage names = _documentNames[subject];
+     264         267 :         uint256 length = names.length;
+     265         267 :         for (uint256 i = 0; i < length; ++i) {
+     266         269 :             if (names[i] == name_) {
+     267         267 :                 names[i] = names[length - 1];
+     268         267 :                 names.pop();
+     269         267 :                 break;
+     270             :             }
+     271             :         }
+     272             :     }
+     273             : 
+     274             :     /**
+     275             :      * @dev Shared removal implementation: reverts {ERC1643MissingDocument} when the document does
+     276             :      * not exist, then emits the address-carrying extension event and clears the entry.
+     277             :      * @param subject The contract the document belongs to.
+     278             :      * @param name_ The document name.
+     279             :      */
+     280         269 :     function _removeDocument(address subject, bytes32 name_) internal virtual {
+     281         269 :         Document storage doc = _documents[subject][name_];
+     282             :         // ERC-1643: reverts when the named document does not exist
+     283         269 :         if (doc.lastModified == 0) {
+     284           2 :             revert ERC1643MissingDocument();
+     285             :         }
+     286             : 
+     287             :         // This engine is a shared, multi-subject manager: per the ERC-1643
+     288             :         // "Emission Responsibility" rules it emits only the address-carrying
+     289             :         // extension event (the base `DocumentRemoved` is the token contract's
+     290             :         // responsibility).
+     291         267 :         emit DocumentRemovedForSubject(subject, name_, doc.uri, doc.documentHash);
+     292             : 
+     293             :         delete _documents[subject][name_];
+     294         267 :         _removeDocumentName(subject, name_);
+     295             :     }
+     296             : 
+     297             :     /**
+     298             :      * @dev Shared create/update implementation: rejects a null `subject` and a null `name_`, tracks
+     299             :      * the name on first write, then stores the document and emits the extension event.
+     300             :      * @param subject The contract the document belongs to.
+     301             :      * @param name_ The document name.
+     302             :      * @param uri_ The document location.
+     303             :      * @param documentHash_ The hash of the document contents.
+     304             :      */
+     305         603 :     function _setDocument(address subject, bytes32 name_, string memory uri_, bytes32 documentHash_) internal virtual {
+     306             :         // Multi-token guard: `subject` must be a real contract address, never the
+     307             :         // null namespace. (The bound-token path passes `_msgSender()`, never zero.)
+     308         603 :         if (subject == address(0)) {
+     309           2 :             revert MultiDocumentInvalidSubject();
+     310             :         }
+     311             :         // ERC-1643: reject the null name (ambiguous / default key)
+     312         601 :         if (name_ == bytes32(0)) {
+     313           3 :             revert ERC1643InvalidName();
+     314             :         }
+     315             : 
+     316         598 :         Document storage doc = _documents[subject][name_];
+     317         598 :         if (doc.lastModified == 0) {
+     318             :             // new document
+     319         587 :             _documentNames[subject].push(name_);
+     320             :         }
+     321         598 :         doc.uri = uri_;
+     322         598 :         doc.documentHash = documentHash_;
+     323         598 :         doc.lastModified = block.timestamp;
+     324             : 
+     325             :         // Shared, multi-subject manager: emit only the address-carrying extension
+     326             :         // event (see the {_removeDocument} note).
+     327         598 :         emit DocumentUpdatedForSubject(subject, name_, uri_, documentHash_);
+     328             :     }
+     329             : 
+     330             :     /*//////////////////////////////////////////////////////////////
+     331             :                         ACCESS CONTROL (hooks)
+     332             :     //////////////////////////////////////////////////////////////*/
+     333             : 
+     334             :     /**
+     335             :      * @dev Authorization hook for the admin document-management path.
+     336             :      * Implemented by the deployment contract (e.g. a role check).
+     337             :      */
+     338           0 :     function _authorizeDocumentManagement() internal view virtual;
+     339             : 
+     340             :     /**
+     341             :      * @dev Authorization hook for the bound-token document-management path.
+     342             :      * Implemented by the deployment contract (e.g. a role check).
+     343             :      */
+     344           0 :     function _authorizeBoundTokenDocumentManagement() internal view virtual;
+     345             : 
+     346             :     /**
+     347             :      * @dev Internal function to fetch a document, as flat values
+     348             :      * @param subject The contract the document belongs to.
+     349             :      * @param name_ The document name.
+     350             :      * @return uri Document location.
+     351             :      * @return documentHash Hash of the document contents.
+     352             :      * @return lastModified Last update timestamp.
+     353             :      */
+     354         803 :     function _getDocument(address subject, bytes32 name_)
+     355             :         internal
+     356             :         view
+     357             :         virtual
+     358             :         returns (string memory uri, bytes32 documentHash, uint256 lastModified)
+     359             :     {
+     360         803 :         Document storage doc = _documents[subject][name_];
+     361         803 :         return (doc.uri, doc.documentHash, doc.lastModified);
+     362             :     }
+     363             : }
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/doc/coverage/coverage/src/DocumentEngineOwnable.sol.func-sort-c.html b/doc/coverage/coverage/src/DocumentEngineOwnable.sol.func-sort-c.html new file mode 100644 index 0000000..e992640 --- /dev/null +++ b/doc/coverage/coverage/src/DocumentEngineOwnable.sol.func-sort-c.html @@ -0,0 +1,92 @@ + + + + + + + LCOV - lcov.info - src/DocumentEngineOwnable.sol - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - src - DocumentEngineOwnable.sol (source / functions)HitTotalCoverage
Test:lcov.infoLines:91181.8 %
Date:2026-08-17 11:57:58Functions:4580.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
DocumentEngineOwnable._msgData0
DocumentEngineOwnable.supportsInterface7
DocumentEngineOwnable._authorizeDocumentManagement8
DocumentEngineOwnable._contextSuffixLength20
DocumentEngineOwnable._msgSender20
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/doc/coverage/coverage/src/DocumentEngineOwnable.sol.func.html b/doc/coverage/coverage/src/DocumentEngineOwnable.sol.func.html new file mode 100644 index 0000000..0f79dbf --- /dev/null +++ b/doc/coverage/coverage/src/DocumentEngineOwnable.sol.func.html @@ -0,0 +1,92 @@ + + + + + + + LCOV - lcov.info - src/DocumentEngineOwnable.sol - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - src - DocumentEngineOwnable.sol (source / functions)HitTotalCoverage
Test:lcov.infoLines:91181.8 %
Date:2026-08-17 11:57:58Functions:4580.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
DocumentEngineOwnable._authorizeDocumentManagement8
DocumentEngineOwnable._contextSuffixLength20
DocumentEngineOwnable._msgData0
DocumentEngineOwnable._msgSender20
DocumentEngineOwnable.supportsInterface7
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/doc/coverage/coverage/src/DocumentEngineOwnable.sol.gcov.html b/doc/coverage/coverage/src/DocumentEngineOwnable.sol.gcov.html new file mode 100644 index 0000000..59ce8c3 --- /dev/null +++ b/doc/coverage/coverage/src/DocumentEngineOwnable.sol.gcov.html @@ -0,0 +1,169 @@ + + + + + + + LCOV - lcov.info - src/DocumentEngineOwnable.sol + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - src - DocumentEngineOwnable.sol (source / functions)HitTotalCoverage
Test:lcov.infoLines:91181.8 %
Date:2026-08-17 11:57:58Functions:4580.0 %
+
+ + + + + + + + +

+
          Line data    Source code
+
+       1             : //SPDX-License-Identifier: MPL-2.0
+       2             : pragma solidity ^0.8.24;
+       3             : 
+       4             : import {Ownable} from "OZ/access/Ownable.sol";
+       5             : import {Ownable2Step} from "OZ/access/Ownable2Step.sol";
+       6             : import {Context} from "OZ/utils/Context.sol";
+       7             : import {ERC2771Context} from "OZ/metatx/ERC2771Context.sol";
+       8             : import {IERC1643} from "CMTAT/interfaces/tokenization/draft-IERC1643.sol";
+       9             : import {IERC1643MultiDocument} from "./interfaces/IERC1643MultiDocument.sol";
+      10             : import {ITokenBinding} from "./interfaces/ITokenBinding.sol";
+      11             : import {TokenBindingModule} from "./modules/TokenBindingModule.sol";
+      12             : import {VersionModule} from "./modules/VersionModule.sol";
+      13             : 
+      14             : /**
+      15             :  * @title DocumentEngineOwnable
+      16             :  * @notice Alternative deployment of the DocumentEngine that uses a single owner
+      17             :  * ({Ownable2Step}) instead of role-based access control.
+      18             :  * @dev Reuses the same document-management logic ({DocumentEngineBase}) and token
+      19             :  * binding ({TokenBindingModule}), swapping only the access-control implementation:
+      20             :  * document management and token binding are both restricted to the `owner`, and a
+      21             :  * bound token manages only its own documents. Ownership uses the two-step transfer
+      22             :  * flow for safety, and the contract also exposes its version through ERC-8303
+      23             :  * ({VersionModule}) and wires ERC-2771.
+      24             :  */
+      25             : contract DocumentEngineOwnable is TokenBindingModule, VersionModule, Ownable2Step, ERC2771Context {
+      26             :     /**
+      27             :      * @notice Deploys the engine with `owner_` as its single privileged account.
+      28             :      * @param owner_ initial owner of the contract
+      29             :      * @param forwarderIrrevocable address of the ERC-2771 forwarder (gasless support)
+      30             :      */
+      31             :     constructor(address owner_, address forwarderIrrevocable) Ownable(owner_) ERC2771Context(forwarderIrrevocable) {}
+      32             : 
+      33             :     /*//////////////////////////////////////////////////////////////
+      34             :                            ERC165
+      35             :     //////////////////////////////////////////////////////////////*/
+      36             : 
+      37             :     /**
+      38             :      * @notice Returns whether this contract implements `interfaceId`.
+      39             :      * @dev ERC-165 discovery: advertises ERC-1643 and its multi-subject extension, the token-binding
+      40             :      * surface and the version module (ERC-8303). See the rationale on
+      41             :      * {DocumentEngine-supportsInterface} for what `type(IERC1643).interfaceId` does and does not
+      42             :      * tell a caller here. See {IERC165-supportsInterface}.
+      43             :      * @param interfaceId The ERC-165 interface identifier to query.
+      44             :      * @return True when `interfaceId` is one of the advertised interfaces or is supported by a base
+      45             :      * contract.
+      46             :      */
+      47           7 :     function supportsInterface(bytes4 interfaceId) public view virtual override(VersionModule) returns (bool) {
+      48           7 :         return interfaceId == type(IERC1643).interfaceId || interfaceId == type(IERC1643MultiDocument).interfaceId
+      49           5 :             || interfaceId == type(ITokenBinding).interfaceId || super.supportsInterface(interfaceId);
+      50             :     }
+      51             : 
+      52             :     /*//////////////////////////////////////////////////////////////
+      53             :                         ACCESS CONTROL (implementation)
+      54             :     //////////////////////////////////////////////////////////////*/
+      55             : 
+      56             :     /**
+      57             :      * @dev Authorization for the admin document-management path (and, via
+      58             :      * {TokenBindingModule}, for token binding): only the owner.
+      59             :      */
+      60           8 :     function _authorizeDocumentManagement() internal view virtual override {
+      61           8 :         _checkOwner();
+      62             :     }
+      63             : 
+      64             :     /*//////////////////////////////////////////////////////////////
+      65             :                            ERC2771
+      66             :     //////////////////////////////////////////////////////////////*/
+      67             : 
+      68             :     /**
+      69             :      * @dev This surcharge is not necessary if you do not use ERC2771
+      70             :      * @return sender The transaction sender, unwrapped from the ERC-2771 calldata suffix when the
+      71             :      * call came through the trusted forwarder.
+      72             :      */
+      73          20 :     function _msgSender() internal view virtual override(ERC2771Context, Context) returns (address sender) {
+      74          20 :         return ERC2771Context._msgSender();
+      75             :     }
+      76             : 
+      77             :     /**
+      78             :      * @dev This surcharge is not necessary if you do not use ERC2771
+      79             :      * @return The calldata, stripped of the ERC-2771 sender suffix when the call came through the
+      80             :      * trusted forwarder.
+      81             :      */
+      82           0 :     function _msgData() internal view virtual override(ERC2771Context, Context) returns (bytes calldata) {
+      83           0 :         return ERC2771Context._msgData();
+      84             :     }
+      85             : 
+      86             :     /**
+      87             :      * @dev This surcharge is not necessary if you do not use the MetaTxModule
+      88             :      * @return The length of the ERC-2771 calldata suffix holding the sender address.
+      89             :      */
+      90          20 :     function _contextSuffixLength() internal view virtual override(ERC2771Context, Context) returns (uint256) {
+      91          20 :         return ERC2771Context._contextSuffixLength();
+      92             :     }
+      93             : }
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/doc/coverage/coverage/src/index-sort-f.html b/doc/coverage/coverage/src/index-sort-f.html index 6d71c92..757b24d 100644 --- a/doc/coverage/coverage/src/index-sort-f.html +++ b/doc/coverage/coverage/src/index-sort-f.html @@ -31,27 +31,18 @@ lcov.info Lines: - 51 - 52 - 98.1 % + 101 + 107 + 94.4 % Date: - 2024-09-09 15:02:57 + 2026-08-17 11:57:58 Functions: - 12 - 14 - 85.7 % - - - - - - Branches: - 13 - 14 - 92.9 % + 28 + 32 + 87.5 % @@ -65,33 +56,48 @@ - - - - - - - - + + + + + + - + + + + + + + + - - + + - - - + + + + + + + + +


Filename Sort by name Line Coverage Sort by line coverage Functions Sort by function coverageBranches Sort by branch coverage
DocumentEngineOwnable.sol +
81.8%81.8%
+
81.8 %9 / 1180.0 %4 / 5
DocumentEngine.sol -
98.1%98.1%
+
89.5%89.5%
98.1 %51 / 5289.5 %17 / 19 85.7 %12 / 1492.9 %13 / 146 / 7
DocumentEngineBase.sol +
97.4%97.4%
+
97.4 %75 / 7790.0 %18 / 20
diff --git a/doc/coverage/coverage/src/index-sort-l.html b/doc/coverage/coverage/src/index-sort-l.html index 9d0c2f2..4ee8603 100644 --- a/doc/coverage/coverage/src/index-sort-l.html +++ b/doc/coverage/coverage/src/index-sort-l.html @@ -31,27 +31,18 @@ lcov.info Lines: - 51 - 52 - 98.1 % + 101 + 107 + 94.4 % Date: - 2024-09-09 15:02:57 + 2026-08-17 11:57:58 Functions: - 12 - 14 - 85.7 % - - - - - - Branches: - 13 - 14 - 92.9 % + 28 + 32 + 87.5 % @@ -65,33 +56,48 @@ - - - - - - - - + + + + + + - + + + + + + + + - - + + - - - + + + + + + + + +


Filename Sort by name Line Coverage Sort by line coverage Functions Sort by function coverageBranches Sort by branch coverage
DocumentEngineOwnable.sol +
81.8%81.8%
+
81.8 %9 / 1180.0 %4 / 5
DocumentEngine.sol -
98.1%98.1%
+
89.5%89.5%
98.1 %51 / 5289.5 %17 / 19 85.7 %12 / 1492.9 %13 / 146 / 7
DocumentEngineBase.sol +
97.4%97.4%
+
97.4 %75 / 7790.0 %18 / 20
diff --git a/doc/coverage/coverage/src/index.html b/doc/coverage/coverage/src/index.html index 64ab2cb..f433b89 100644 --- a/doc/coverage/coverage/src/index.html +++ b/doc/coverage/coverage/src/index.html @@ -31,27 +31,18 @@ lcov.info Lines: - 51 - 52 - 98.1 % + 101 + 107 + 94.4 % Date: - 2024-09-09 15:02:57 + 2026-08-17 11:57:58 Functions: - 12 - 14 - 85.7 % - - - - - - Branches: - 13 - 14 - 92.9 % + 28 + 32 + 87.5 % @@ -65,33 +56,48 @@ - - - - - - - - + + + + + + - - - + + - - - + + + + + + + + + + + + + + + + +


Filename Sort by name Line Coverage Sort by line coverage Functions Sort by function coverageBranches Sort by branch coverage
DocumentEngine.sol -
98.1%98.1%
+
89.5%89.5%
98.1 %51 / 5289.5 %17 / 19 85.7 %12 / 1492.9 %13 / 146 / 7
DocumentEngineBase.sol +
97.4%97.4%
+
97.4 %75 / 7790.0 %18 / 20
DocumentEngineOwnable.sol +
81.8%81.8%
+
81.8 %9 / 1180.0 %4 / 5
diff --git a/doc/coverage/coverage/src/modules/TokenBindingModule.sol.func-sort-c.html b/doc/coverage/coverage/src/modules/TokenBindingModule.sol.func-sort-c.html new file mode 100644 index 0000000..7859b5a --- /dev/null +++ b/doc/coverage/coverage/src/modules/TokenBindingModule.sol.func-sort-c.html @@ -0,0 +1,96 @@ + + + + + + + LCOV - lcov.info - src/modules/TokenBindingModule.sol - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - src/modules - TokenBindingModule.sol (source / functions)HitTotalCoverage
Test:lcov.infoLines:2020100.0 %
Date:2026-08-17 11:57:58Functions:66100.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
TokenBindingModule.unbindToken6
TokenBindingModule._checkTokenBound9
TokenBindingModule._authorizeBoundTokenDocumentManagement10
TokenBindingModule.isTokenBound10
TokenBindingModule.bindToken12
TokenBindingModule._setTokenBinding16
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/doc/coverage/coverage/src/modules/TokenBindingModule.sol.func.html b/doc/coverage/coverage/src/modules/TokenBindingModule.sol.func.html new file mode 100644 index 0000000..3bb1820 --- /dev/null +++ b/doc/coverage/coverage/src/modules/TokenBindingModule.sol.func.html @@ -0,0 +1,96 @@ + + + + + + + LCOV - lcov.info - src/modules/TokenBindingModule.sol - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - src/modules - TokenBindingModule.sol (source / functions)HitTotalCoverage
Test:lcov.infoLines:2020100.0 %
Date:2026-08-17 11:57:58Functions:66100.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
TokenBindingModule._authorizeBoundTokenDocumentManagement10
TokenBindingModule._checkTokenBound9
TokenBindingModule._setTokenBinding16
TokenBindingModule.bindToken12
TokenBindingModule.isTokenBound10
TokenBindingModule.unbindToken6
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/doc/coverage/coverage/src/modules/TokenBindingModule.sol.gcov.html b/doc/coverage/coverage/src/modules/TokenBindingModule.sol.gcov.html new file mode 100644 index 0000000..38cd0c1 --- /dev/null +++ b/doc/coverage/coverage/src/modules/TokenBindingModule.sol.gcov.html @@ -0,0 +1,169 @@ + + + + + + + LCOV - lcov.info - src/modules/TokenBindingModule.sol + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - src/modules - TokenBindingModule.sol (source / functions)HitTotalCoverage
Test:lcov.infoLines:2020100.0 %
Date:2026-08-17 11:57:58Functions:66100.0 %
+
+ + + + + + + + +

+
          Line data    Source code
+
+       1             : // SPDX-License-Identifier: MPL-2.0
+       2             : pragma solidity ^0.8.24;
+       3             : 
+       4             : import {DocumentEngineBase} from "../DocumentEngineBase.sol";
+       5             : import {ITokenBinding} from "../interfaces/ITokenBinding.sol";
+       6             : 
+       7             : /**
+       8             :  * @title TokenBindingModule
+       9             :  * @notice Shared token-binding registry (an allowlist) implementing {ITokenBinding},
+      10             :  * used by every DocumentEngine deployment so binding behaves identically — same
+      11             :  * functions, same event, same revert — regardless of the access-control model.
+      12             :  * @dev A *bound* token may manage its own documents through the standard
+      13             :  * single-argument ERC-1643 functions (`msg.sender` is the token). This module:
+      14             :  *  - stores the allowlist and implements `bindToken` / `unbindToken` / `isTokenBound`;
+      15             :  *  - wires the base bound-token hook ({_authorizeBoundTokenDocumentManagement}) to
+      16             :  *    the allowlist ({_checkTokenBound});
+      17             :  *  - gates binding management with the deployment's document-management
+      18             :  *    authorization ({_authorizeDocumentManagement}), so whoever may manage
+      19             :  *    documents may also decide bindings. It is therefore access-control agnostic:
+      20             :  *    the deployment only implements {_authorizeDocumentManagement}.
+      21             :  */
+      22             : abstract contract TokenBindingModule is DocumentEngineBase, ITokenBinding {
+      23             :     /// @dev Tokens bound to the engine, allowed to manage their own documents.
+      24             :     mapping(address => bool) private _boundTokens;
+      25             : 
+      26             :     /// @notice Thrown when a non-bound caller attempts a bound-token operation.
+      27             :     error NotBoundToken(address caller);
+      28             : 
+      29             :     /**
+      30             :      * @inheritdoc ITokenBinding
+      31             :      * @dev Authorized by the deployment's document-management check.
+      32             :      */
+      33          12 :     function bindToken(address token) external virtual override {
+      34          12 :         _authorizeDocumentManagement();
+      35          10 :         _setTokenBinding(token, true);
+      36             :     }
+      37             : 
+      38             :     /**
+      39             :      * @inheritdoc ITokenBinding
+      40             :      * @dev Authorized by the deployment's document-management check.
+      41             :      */
+      42           6 :     function unbindToken(address token) external virtual override {
+      43           6 :         _authorizeDocumentManagement();
+      44           6 :         _setTokenBinding(token, false);
+      45             :     }
+      46             : 
+      47             :     /**
+      48             :      * @inheritdoc ITokenBinding
+      49             :      */
+      50          10 :     function isTokenBound(address token) public view virtual override returns (bool) {
+      51          10 :         return _boundTokens[token];
+      52             :     }
+      53             : 
+      54             :     /**
+      55             :      * @dev Shared bind/unbind implementation.
+      56             :      *
+      57             :      * Rejects the null address: `address(0)` can never call the engine, so binding it grants
+      58             :      * nothing, but it would still emit a {TokenBindingSet} that off-chain indexers key on — the
+      59             :      * same data-integrity argument the multi-subject draft makes for rejecting a null `subject`.
+      60             :      *
+      61             :      * Writing and emitting only on an actual change makes both functions idempotent and keeps the
+      62             :      * event stream free of no-op entries, so an indexer can treat every {TokenBindingSet} as a real
+      63             :      * transition rather than having to de-duplicate. The repeated call still succeeds, since the
+      64             :      * caller's intent — "this token is (not) bound" — already holds.
+      65             :      * @param token The token whose binding is being set.
+      66             :      * @param bound The binding state to apply: `true` to bind, `false` to unbind.
+      67             :      */
+      68          16 :     function _setTokenBinding(address token, bool bound) internal virtual {
+      69          16 :         if (token == address(0)) {
+      70           2 :             revert TokenBindingInvalidToken();
+      71             :         }
+      72          14 :         if (_boundTokens[token] == bound) {
+      73          14 :             return;
+      74             :         }
+      75          11 :         _boundTokens[token] = bound;
+      76          11 :         emit TokenBindingSet(token, bound);
+      77             :     }
+      78             : 
+      79             :     /**
+      80             :      * @dev Bound-token document-management authorization: the caller
+      81             :      * (`_msgSender()`) must be a bound token.
+      82             :      */
+      83          10 :     function _authorizeBoundTokenDocumentManagement() internal view virtual override {
+      84          10 :         _checkTokenBound();
+      85             :     }
+      86             : 
+      87             :     /// @dev Reverts {NotBoundToken} if the caller (`_msgSender()`) is not bound.
+      88           9 :     function _checkTokenBound() internal view virtual {
+      89           9 :         if (!_boundTokens[_msgSender()]) {
+      90           4 :             revert NotBoundToken(_msgSender());
+      91             :         }
+      92             :     }
+      93             : }
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/doc/coverage/coverage/src/modules/VersionModule.sol.func-sort-c.html b/doc/coverage/coverage/src/modules/VersionModule.sol.func-sort-c.html new file mode 100644 index 0000000..32f4c60 --- /dev/null +++ b/doc/coverage/coverage/src/modules/VersionModule.sol.func-sort-c.html @@ -0,0 +1,80 @@ + + + + + + + LCOV - lcov.info - src/modules/VersionModule.sol - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - src/modules - VersionModule.sol (source / functions)HitTotalCoverage
Test:lcov.infoLines:44100.0 %
Date:2026-08-17 11:57:58Functions:22100.0 %
+
+ +
+ + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
VersionModule.version6
VersionModule.supportsInterface7
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/doc/coverage/coverage/src/modules/VersionModule.sol.func.html b/doc/coverage/coverage/src/modules/VersionModule.sol.func.html new file mode 100644 index 0000000..7f9d579 --- /dev/null +++ b/doc/coverage/coverage/src/modules/VersionModule.sol.func.html @@ -0,0 +1,80 @@ + + + + + + + LCOV - lcov.info - src/modules/VersionModule.sol - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - src/modules - VersionModule.sol (source / functions)HitTotalCoverage
Test:lcov.infoLines:44100.0 %
Date:2026-08-17 11:57:58Functions:22100.0 %
+
+ +
+ + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
VersionModule.supportsInterface7
VersionModule.version6
+
+
+ + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/doc/coverage/coverage/src/modules/VersionModule.sol.gcov.html b/doc/coverage/coverage/src/modules/VersionModule.sol.gcov.html new file mode 100644 index 0000000..f6fa800 --- /dev/null +++ b/doc/coverage/coverage/src/modules/VersionModule.sol.gcov.html @@ -0,0 +1,116 @@ + + + + + + + LCOV - lcov.info - src/modules/VersionModule.sol + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - src/modules - VersionModule.sol (source / functions)HitTotalCoverage
Test:lcov.infoLines:44100.0 %
Date:2026-08-17 11:57:58Functions:22100.0 %
+
+ + + + + + + + +

+
          Line data    Source code
+
+       1             : // SPDX-License-Identifier: MPL-2.0
+       2             : pragma solidity ^0.8.24;
+       3             : 
+       4             : import {ERC165} from "OZ/utils/introspection/ERC165.sol";
+       5             : import {IERC8303} from "../interfaces/IERC8303.sol";
+       6             : 
+       7             : /**
+       8             :  * @title VersionModule
+       9             :  * @notice Exposes the current contract version through ERC-8303 (`version()`),
+      10             :  * with optional ERC-165 interface discovery.
+      11             :  * @dev Implements ERC-8303 (Draft). The version string is defined here so the
+      12             :  * version concern is isolated in a dedicated module (CMTAT pattern). A deployment
+      13             :  * contract that also implements ERC-165 must combine this module's
+      14             :  * {supportsInterface} with the others it inherits.
+      15             :  */
+      16             : abstract contract VersionModule is IERC8303, ERC165 {
+      17             :     /**
+      18             :      * @notice Get the current version of the smart contract.
+      19             :      * @dev Follows Semantic Versioning 2.0.0 (`MAJOR.MINOR.PATCH`).
+      20             :      */
+      21             :     string public constant VERSION = "0.4.0";
+      22             : 
+      23             :     /**
+      24             :      * @inheritdoc IERC8303
+      25             :      */
+      26           6 :     function version() public view virtual override(IERC8303) returns (string memory version_) {
+      27           6 :         return VERSION;
+      28             :     }
+      29             : 
+      30             :     /**
+      31             :      * @notice Returns whether this contract implements `interfaceId`.
+      32             :      * @dev Advertises ERC-8303 support (interface id `0x54fd4d50`).
+      33             :      * See {IERC165-supportsInterface}.
+      34             :      * @param interfaceId The ERC-165 interface identifier to query.
+      35             :      * @return True when `interfaceId` is ERC-8303 or is supported by a base contract.
+      36             :      */
+      37           7 :     function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
+      38           7 :         return interfaceId == type(IERC8303).interfaceId || super.supportsInterface(interfaceId);
+      39             :     }
+      40             : }
+
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/doc/coverage/coverage/src/modules/index-sort-f.html b/doc/coverage/coverage/src/modules/index-sort-f.html new file mode 100644 index 0000000..4fd74b9 --- /dev/null +++ b/doc/coverage/coverage/src/modules/index-sort-f.html @@ -0,0 +1,103 @@ + + + + + + + LCOV - lcov.info - src/modules + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - src/modulesHitTotalCoverage
Test:lcov.infoLines:2424100.0 %
Date:2026-08-17 11:57:58Functions:88100.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Filename Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverage
VersionModule.sol +
100.0%
+
100.0 %4 / 4100.0 %2 / 2
TokenBindingModule.sol +
100.0%
+
100.0 %20 / 20100.0 %6 / 6
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/doc/coverage/coverage/src/modules/index-sort-l.html b/doc/coverage/coverage/src/modules/index-sort-l.html new file mode 100644 index 0000000..3418270 --- /dev/null +++ b/doc/coverage/coverage/src/modules/index-sort-l.html @@ -0,0 +1,103 @@ + + + + + + + LCOV - lcov.info - src/modules + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - src/modulesHitTotalCoverage
Test:lcov.infoLines:2424100.0 %
Date:2026-08-17 11:57:58Functions:88100.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Filename Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverage
VersionModule.sol +
100.0%
+
100.0 %4 / 4100.0 %2 / 2
TokenBindingModule.sol +
100.0%
+
100.0 %20 / 20100.0 %6 / 6
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/doc/coverage/coverage/src/modules/index.html b/doc/coverage/coverage/src/modules/index.html new file mode 100644 index 0000000..0964418 --- /dev/null +++ b/doc/coverage/coverage/src/modules/index.html @@ -0,0 +1,103 @@ + + + + + + + LCOV - lcov.info - src/modules + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - src/modulesHitTotalCoverage
Test:lcov.infoLines:2424100.0 %
Date:2026-08-17 11:57:58Functions:88100.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Filename Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverage
TokenBindingModule.sol +
100.0%
+
100.0 %20 / 20100.0 %6 / 6
VersionModule.sol +
100.0%
+
100.0 %4 / 4100.0 %2 / 2
+
+
+ + + + +
Generated by: LCOV version 1.16
+
+ + + diff --git a/doc/coverage/lcov.info b/doc/coverage/lcov.info index ba3dafb..10c4cb1 100644 --- a/doc/coverage/lcov.info +++ b/doc/coverage/lcov.info @@ -1,185 +1,246 @@ TN: SF:src/DocumentEngine.sol -FN:46,DocumentEngine.setDocument -FNDA:28,DocumentEngine.setDocument -DA:52,27 -DA:52,27 -FN:58,DocumentEngine.removeDocument -FNDA:2,DocumentEngine.removeDocument -DA:62,1 -DA:62,1 -FN:68,DocumentEngine.batchSetDocuments -FNDA:8,DocumentEngine.batchSetDocuments -DA:75,7 -DA:75,7 -DA:75,7 -DA:75,7 -DA:75,7 -DA:76,6 -DA:76,6 -DA:77,5 -DA:77,5 -DA:78,4 -DA:78,4 -BRDA:74,0,0,3 -BRDA:74,0,1,4 -DA:80,3 -DA:80,3 -DA:82,4 -DA:82,4 -DA:82,12 -DA:82,8 -DA:83,8 -DA:83,8 -FN:90,DocumentEngine.batchSetDocuments -FNDA:5,DocumentEngine.batchSetDocuments -DA:97,4 -DA:97,4 -DA:97,4 -DA:97,4 -DA:98,3 -DA:98,3 -DA:99,2 -DA:99,2 -BRDA:96,1,0,2 -BRDA:96,1,1,2 -DA:101,2 -DA:101,2 -DA:103,2 -DA:103,2 -DA:103,6 -DA:103,4 -DA:104,4 -DA:104,4 -FN:111,DocumentEngine.batchRemoveDocuments -FNDA:4,DocumentEngine.batchRemoveDocuments -DA:116,3 -DA:116,3 -DA:116,3 -BRDA:115,2,0,2 -BRDA:115,2,1,1 -DA:119,2 -DA:119,2 -DA:122,1 -DA:122,1 -DA:122,3 -DA:122,2 -DA:123,2 -DA:123,2 -FN:130,DocumentEngine.batchRemoveDocuments -FNDA:3,DocumentEngine.batchRemoveDocuments -DA:134,2 -DA:134,2 -BRDA:134,3,0,1 -BRDA:134,3,1,1 -DA:135,1 -DA:135,1 -DA:138,1 -DA:138,1 -DA:138,3 +FN:39,DocumentEngine.constructor +FN:72,DocumentEngine.hasRole +FN:114,DocumentEngine.supportsInterface +FN:133,DocumentEngine._authorizeDocumentManagement +FN:146,DocumentEngine._msgSender +FN:155,DocumentEngine._msgData +FN:163,DocumentEngine._contextSuffixLength +FNDA:8,DocumentEngine.supportsInterface +FNDA:64,DocumentEngine.constructor +FNDA:0,DocumentEngine._msgData +FNDA:966,DocumentEngine._contextSuffixLength +FNDA:966,DocumentEngine._msgSender +FNDA:882,DocumentEngine._authorizeDocumentManagement +FNDA:68,DocumentEngine.hasRole +FNF:7 +FNH:6 +DA:39,64 +DA:40,64 +DA:41,1 +DA:43,63 +DA:72,68 +DA:80,952 +DA:81,882 +DA:83,70 +DA:114,8 +DA:121,8 +DA:122,5 +DA:133,882 +DA:134,882 +DA:146,966 +DA:147,966 +DA:155,0 +DA:156,0 +DA:163,966 +DA:164,966 +LF:19 +LH:17 +end_of_record +TN: +SF:src/DocumentEngineBase.sol +FN:43,DocumentEngineBase.onlyDocumentManager +FN:53,DocumentEngineBase.onlyBoundToken +FN:67,DocumentEngineBase.removeDocument.0 +FN:84,DocumentEngineBase.setDocument.0 +FN:93,DocumentEngineBase.removeDocument.1 +FN:105,DocumentEngineBase.batchSetDocuments.0 +FN:131,DocumentEngineBase.batchSetDocuments.1 +FN:152,DocumentEngineBase.batchRemoveDocuments.0 +FN:169,DocumentEngineBase.batchRemoveDocuments.1 +FN:190,DocumentEngineBase.getDocument.0 +FN:208,DocumentEngineBase.getDocument.1 +FN:221,DocumentEngineBase.getAllDocuments.0 +FN:230,DocumentEngineBase.getAllDocuments.1 +FN:245,DocumentEngineBase.setDocument.1 +FN:262,DocumentEngineBase._removeDocumentName +FN:280,DocumentEngineBase._removeDocument +FN:305,DocumentEngineBase._setDocument +FN:338,DocumentEngineBase._authorizeDocumentManagement +FN:344,DocumentEngineBase._authorizeBoundTokenDocumentManagement +FN:354,DocumentEngineBase._getDocument +FNDA:798,DocumentEngineBase.getDocument.1 +FNDA:7,DocumentEngineBase.setDocument.0 +FNDA:5,DocumentEngineBase.batchRemoveDocuments.0 +FNDA:266,DocumentEngineBase.getAllDocuments.1 +FNDA:7,DocumentEngineBase.onlyBoundToken +FNDA:3,DocumentEngineBase.getAllDocuments.0 +FNDA:263,DocumentEngineBase.removeDocument.0 +FNDA:10,DocumentEngineBase.batchSetDocuments.0 +FNDA:269,DocumentEngineBase._removeDocument +FNDA:5,DocumentEngineBase.batchSetDocuments.1 +FNDA:3,DocumentEngineBase.onlyDocumentManager +FNDA:3,DocumentEngineBase.removeDocument.1 +FNDA:587,DocumentEngineBase.setDocument.1 +FNDA:0,DocumentEngineBase._authorizeDocumentManagement +FNDA:803,DocumentEngineBase._getDocument +FNDA:3,DocumentEngineBase.batchRemoveDocuments.1 +FNDA:0,DocumentEngineBase._authorizeBoundTokenDocumentManagement +FNDA:267,DocumentEngineBase._removeDocumentName +FNDA:603,DocumentEngineBase._setDocument +FNDA:5,DocumentEngineBase.getDocument.0 +FNF:20 +FNH:18 +DA:43,3 +DA:44,3 +DA:53,7 +DA:54,7 +DA:67,263 +DA:68,262 +DA:84,7 +DA:85,4 +DA:93,3 +DA:94,2 +DA:105,10 +DA:112,9 +DA:113,6 +DA:114,3 +DA:115,3 +DA:117,6 +DA:118,6 +DA:119,10 +DA:131,5 +DA:137,4 DA:138,2 -DA:139,2 -DA:139,2 -FN:146,DocumentEngine.getDocument -FNDA:1,DocumentEngine.getDocument -DA:149,1 -DA:149,1 -DA:149,1 -FN:155,DocumentEngine.getDocument -FNDA:19,DocumentEngine.getDocument -DA:159,19 -DA:159,19 -DA:159,19 -FN:165,DocumentEngine.getAllDocuments -FNDA:1,DocumentEngine.getAllDocuments +DA:140,2 +DA:141,2 +DA:142,4 +DA:152,5 +DA:153,4 +DA:154,2 +DA:157,2 +DA:158,2 +DA:159,3 +DA:169,3 +DA:170,2 DA:171,1 -DA:171,1 -FN:177,DocumentEngine.getAllDocuments -FNDA:7,DocumentEngine.getAllDocuments -DA:180,7 -DA:180,7 -FN:187,DocumentEngine.hasRole -FNDA:0,DocumentEngine.hasRole -DA:192,50 -DA:192,50 -BRDA:192,4,0,44 -BRDA:192,4,1,6 -DA:193,44 -DA:193,44 -DA:195,6 -DA:195,6 -DA:195,6 -FN:205,DocumentEngine._getDocument -FNDA:20,DocumentEngine._getDocument -DA:209,20 -DA:209,20 -DA:210,20 -DA:210,20 -FN:216,DocumentEngine._removeDocumentName -FNDA:5,DocumentEngine._removeDocumentName -DA:220,5 -DA:220,5 -DA:221,5 -DA:221,5 -DA:221,5 -DA:221,0 -DA:222,5 -DA:222,5 -BRDA:222,5,0,- -BRDA:222,5,1,5 -DA:223,5 -DA:223,5 -DA:226,5 -DA:226,5 -DA:227,5 -DA:227,5 -FN:232,DocumentEngine._removeDocument -FNDA:5,DocumentEngine._removeDocument -DA:233,5 -DA:233,5 -DA:234,5 -DA:234,5 -DA:236,5 -DA:236,5 -DA:237,5 -DA:237,5 -FN:240,DocumentEngine._setDocument -FNDA:39,DocumentEngine._setDocument -DA:246,39 -DA:246,39 -DA:247,39 -DA:247,39 -BRDA:247,6,0,30 -BRDA:247,6,1,39 -DA:249,30 -DA:249,30 -DA:251,39 -DA:251,39 -DA:252,39 -DA:252,39 -DA:253,39 -DA:253,39 -DA:254,39 -DA:254,39 -FN:264,DocumentEngine._msgSender -FNDA:50,DocumentEngine._msgSender -DA:270,50 -DA:270,50 -DA:270,50 -FN:276,DocumentEngine._msgData -FNDA:0,DocumentEngine._msgData -DA:282,0 -DA:282,0 -DA:282,0 -FN:288,DocumentEngine._contextSuffixLength -FNDA:50,DocumentEngine._contextSuffixLength -DA:294,50 -DA:294,50 -DA:294,50 -FNF:18 -FNH:16 -LF:52 -LH:51 -BRF:14 -BRH:13 +DA:174,1 +DA:175,1 +DA:176,2 +DA:190,5 +DA:196,5 +DA:208,798 +DA:214,798 +DA:221,3 +DA:222,3 +DA:230,266 +DA:231,266 +DA:245,587 +DA:250,585 +DA:262,267 +DA:263,267 +DA:264,267 +DA:265,267 +DA:266,269 +DA:267,267 +DA:268,267 +DA:269,267 +DA:280,269 +DA:281,269 +DA:283,269 +DA:284,2 +DA:291,267 +DA:294,267 +DA:305,603 +DA:308,603 +DA:309,2 +DA:312,601 +DA:313,3 +DA:316,598 +DA:317,598 +DA:319,587 +DA:321,598 +DA:322,598 +DA:323,598 +DA:327,598 +DA:338,0 +DA:344,0 +DA:354,803 +DA:360,803 +DA:361,803 +LF:77 +LH:75 +end_of_record +TN: +SF:src/DocumentEngineOwnable.sol +FN:47,DocumentEngineOwnable.supportsInterface +FN:60,DocumentEngineOwnable._authorizeDocumentManagement +FN:73,DocumentEngineOwnable._msgSender +FN:82,DocumentEngineOwnable._msgData +FN:90,DocumentEngineOwnable._contextSuffixLength +FNDA:0,DocumentEngineOwnable._msgData +FNDA:20,DocumentEngineOwnable._contextSuffixLength +FNDA:20,DocumentEngineOwnable._msgSender +FNDA:7,DocumentEngineOwnable.supportsInterface +FNDA:8,DocumentEngineOwnable._authorizeDocumentManagement +FNF:5 +FNH:4 +DA:47,7 +DA:48,7 +DA:49,5 +DA:60,8 +DA:61,8 +DA:73,20 +DA:74,20 +DA:82,0 +DA:83,0 +DA:90,20 +DA:91,20 +LF:11 +LH:9 +end_of_record +TN: +SF:src/modules/TokenBindingModule.sol +FN:33,TokenBindingModule.bindToken +FN:42,TokenBindingModule.unbindToken +FN:50,TokenBindingModule.isTokenBound +FN:68,TokenBindingModule._setTokenBinding +FN:83,TokenBindingModule._authorizeBoundTokenDocumentManagement +FN:88,TokenBindingModule._checkTokenBound +FNDA:10,TokenBindingModule.isTokenBound +FNDA:9,TokenBindingModule._checkTokenBound +FNDA:16,TokenBindingModule._setTokenBinding +FNDA:6,TokenBindingModule.unbindToken +FNDA:10,TokenBindingModule._authorizeBoundTokenDocumentManagement +FNDA:12,TokenBindingModule.bindToken +FNF:6 +FNH:6 +DA:33,12 +DA:34,12 +DA:35,10 +DA:42,6 +DA:43,6 +DA:44,6 +DA:50,10 +DA:51,10 +DA:68,16 +DA:69,16 +DA:70,2 +DA:72,14 +DA:73,14 +DA:75,11 +DA:76,11 +DA:83,10 +DA:84,10 +DA:88,9 +DA:89,9 +DA:90,4 +LF:20 +LH:20 +end_of_record +TN: +SF:src/modules/VersionModule.sol +FN:26,VersionModule.version +FN:37,VersionModule.supportsInterface +FNDA:7,VersionModule.supportsInterface +FNDA:6,VersionModule.version +FNF:2 +FNH:2 +DA:26,6 +DA:27,6 +DA:37,7 +DA:38,7 +LF:4 +LH:4 end_of_record From a65e637a2c58d1e468c0fad3faf7e3fcffbc14af Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:08:21 +0200 Subject: [PATCH 37/47] docs: retire IMPROVEMENT.md into AUDIT_OVERVIEW open items, add CMTAT integration diagrams --- AGENTS.md | 13 ++- CHANGELOG.md | 9 +- CLAUDE.md | 13 ++- README.md | 68 +++++++++--- doc/audits/AUDIT_OVERVIEW.md | 19 ++-- .../v0.4.0/aderyn/aderyn-report-feedback.md | 6 +- .../tools/v0.4.0/aderyn/aderyn-report.md | 4 +- .../tools/v0.4.0/claude/CLAUDE_ANALYSIS.md | 4 +- doc/img/cmtat-integration-architecture.png | Bin 0 -> 40084 bytes doc/img/cmtat-integration-architecture.puml | 74 +++++++++++++ doc/img/cmtat-integration-sequence.png | Bin 0 -> 92021 bytes doc/img/cmtat-integration-sequence.puml | 100 ++++++++++++++++++ 12 files changed, 266 insertions(+), 44 deletions(-) create mode 100644 doc/img/cmtat-integration-architecture.png create mode 100644 doc/img/cmtat-integration-architecture.puml create mode 100644 doc/img/cmtat-integration-sequence.png create mode 100644 doc/img/cmtat-integration-sequence.puml diff --git a/AGENTS.md b/AGENTS.md index 5bc269e..36fa1e4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -159,7 +159,10 @@ Other important files: - `remappings.txt` — `CMTAT/`, `RuleEngine/`, `OZ/`, `@openzeppelin/contracts-upgradeable/`. - `CHANGELOG.md` — semver history; update on every release (current: `v0.4.0`). - `ERC-1643-proposition.md` — proposed optional multi-token events / extension. -- `README.md` — full documentation and Surya schema. +- `README.md` — full documentation, Surya schema, and the CMTAT integration diagrams. +- `doc/img/` — PlantUML **sources** (`*.puml`) plus their rendered `*.png`. The README embeds only + the images. Re-render with `plantuml -tpng doc/img/.puml` after editing a source, and look + at the PNG: PlantUML draws syntax/deprecation warnings *into* the image and still exits 0. - `doc/` — Surya output in `doc/surya/{surya_graph,surya_inheritance,surya_report}`, one file per `.sol` in `src/` (9 each), regenerated by the three scripts in `doc/script/` — run them from that directory, **graph first** (it creates the scratch `docOut/`; the report script's `mkdir` lacks @@ -177,10 +180,10 @@ Other important files: its "left as is" rows before proposing an optimisation: `unchecked {++i}` (0 gas on solc 0.8.34), `string calldata` on the admin `setDocument` (49 gas *worse*), and extracting the duplicated ERC-2771 overrides (impossible — C3 linearization) are all measured dead ends. -- `IMPROVEMENT.md` — the open items: deviations from the two ERC specifications, - with severity, effort and a recommendation for each. Update it when an item is - fixed (move the record to `CHANGELOG.md` and `doc/audits/AUDIT_OVERVIEW.md`) - or when review surfaces a new one. +- **Open items live in `doc/audits/AUDIT_OVERVIEW.md`** under *Known open items*, with stable + `OPEN-n` ids that the audit reports cite. There is no `IMPROVEMENT.md` — it was folded in. + Update that table when an item is fixed (move the record to `CHANGELOG.md`) or when review + surfaces a new one; keep the existing ids stable so the citations stay valid. - `lib/` — submodules: `CMTAT`, `RuleEngine`, `openzeppelin-contracts(-upgradeable)`, `forge-std`. ## Dependencies (tested versions) diff --git a/CHANGELOG.md b/CHANGELOG.md index c822893..bffcf5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -178,7 +178,7 @@ for which CMTAT release each version of this engine is built against. ### Added - **Bound-token document management**: implement the now-mandatory `IERC1643.setDocument(name, uri, hash)` and `removeDocument(name)`, gated by the `onlyBoundToken` modifier and scoped to the caller (`_msgSender()`) own namespace. A token bound with `bindToken(token)` (see the shared binding module below) manages its own documents and can never affect another contract's documents. The admin overloads (explicit `address`, `DOCUMENT_MANAGER_ROLE`) are unchanged, so both systems work side by side. (RuleEngine's `ERC3643ComplianceExtendedModule` was evaluated for the binding but intentionally not reused — see the README.) -- **Optional multi-token events**: alongside the standard `IERC1643` events, the engine now also emits `DocumentUpdatedForContract` / `DocumentRemovedForContract`, which carry the `smartContract` (token) address so off-chain indexers can tell which contract a document belongs to during multi-contract operations. See [`ERC-1643-proposition.md`](./doc/ERCSpecification/ERC-1643-proposition.md) for the proposed optional standard extension. +- **Optional multi-token events**: alongside the standard `IERC1643` events, the engine now also emits `DocumentUpdatedForContract` / `DocumentRemovedForContract`, which carry the `smartContract` (token) address so off-chain indexers can tell which contract a document belongs to during multi-contract operations. See [`erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md) for the proposed optional standard extension. - **Flexible access control (CMTAT / RuleEngine pattern)**: the restricted functions use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to overridable `internal virtual` authorization hooks `_authorizeDocumentManagement()` / `_authorizeBoundTokenDocumentManagement()`. Each deployment implements the admin hook (`DOCUMENT_MANAGER_ROLE` or `owner`); the bound-token hook is implemented once by `TokenBindingModule` (the shared allowlist). This separates the document-management implementation from the authorization logic. - **Split into a base contract and a deployment contract** (CMTAT module/deployment pattern): the document-management logic and storage now live in the new abstract `DocumentEngineBase` (with abstract `_authorize*` hooks), while `DocumentEngine` is the deployment contract that defines the access control (`AccessControl`, the concrete hooks and `hasRole`) and the ERC-2771 wiring. The deployable `DocumentEngine` API and behavior are unchanged. - **Version module implementing ERC-8303**: the version is now exposed through a dedicated `VersionModule` (`src/modules/VersionModule.sol`) implementing the `IERC8303` interface (`src/interfaces/IERC8303.sol`). It adds a standard `version()` view function (in addition to the existing public `VERSION` constant) and advertises ERC-8303 via ERC-165 (`supportsInterface(0x54fd4d50) == true`). `DocumentEngine` combines the module's `supportsInterface` with the access-control base. @@ -213,9 +213,10 @@ Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/er subject-initiated call topology is fully conformant with the multi-subject draft's *Emission Responsibility* rules. The **admin path remains non-conformant by construction** — a write sent straight to the engine has no execution point in the subject, so the subject emits nothing. - See [`IMPROVEMENT.md`](./IMPROVEMENT.md) item 2. -- Open items are tracked in [`IMPROVEMENT.md`](./IMPROVEMENT.md): the most severe is admin-path call - topology (item 2); also authorization granularity (item 1) and enumeration cost (item 4). + See `OPEN-2` in [`AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md). +- Open items are tracked under *Known open items* in + [`AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md): the most severe is admin-path call topology + (`OPEN-2`); also authorization granularity (`OPEN-1`) and enumeration cost (`OPEN-4`). - CMTAT v3 no longer ships a *standalone* token that consumes an external document engine through its constructor; the standard token stores documents on-chain (`DocumentERC1643Module`). External-engine integration now goes through CMTAT's `DocumentEngineModule` (`setDocumentEngine`). The test suite was updated to exercise this real integration path via a minimal token built on `DocumentEngineModule`. ## v0.3.0 diff --git a/CLAUDE.md b/CLAUDE.md index 5bc269e..36fa1e4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -159,7 +159,10 @@ Other important files: - `remappings.txt` — `CMTAT/`, `RuleEngine/`, `OZ/`, `@openzeppelin/contracts-upgradeable/`. - `CHANGELOG.md` — semver history; update on every release (current: `v0.4.0`). - `ERC-1643-proposition.md` — proposed optional multi-token events / extension. -- `README.md` — full documentation and Surya schema. +- `README.md` — full documentation, Surya schema, and the CMTAT integration diagrams. +- `doc/img/` — PlantUML **sources** (`*.puml`) plus their rendered `*.png`. The README embeds only + the images. Re-render with `plantuml -tpng doc/img/.puml` after editing a source, and look + at the PNG: PlantUML draws syntax/deprecation warnings *into* the image and still exits 0. - `doc/` — Surya output in `doc/surya/{surya_graph,surya_inheritance,surya_report}`, one file per `.sol` in `src/` (9 each), regenerated by the three scripts in `doc/script/` — run them from that directory, **graph first** (it creates the scratch `docOut/`; the report script's `mkdir` lacks @@ -177,10 +180,10 @@ Other important files: its "left as is" rows before proposing an optimisation: `unchecked {++i}` (0 gas on solc 0.8.34), `string calldata` on the admin `setDocument` (49 gas *worse*), and extracting the duplicated ERC-2771 overrides (impossible — C3 linearization) are all measured dead ends. -- `IMPROVEMENT.md` — the open items: deviations from the two ERC specifications, - with severity, effort and a recommendation for each. Update it when an item is - fixed (move the record to `CHANGELOG.md` and `doc/audits/AUDIT_OVERVIEW.md`) - or when review surfaces a new one. +- **Open items live in `doc/audits/AUDIT_OVERVIEW.md`** under *Known open items*, with stable + `OPEN-n` ids that the audit reports cite. There is no `IMPROVEMENT.md` — it was folded in. + Update that table when an item is fixed (move the record to `CHANGELOG.md`) or when review + surfaces a new one; keep the existing ids stable so the citations stay valid. - `lib/` — submodules: `CMTAT`, `RuleEngine`, `openzeppelin-contracts(-upgradeable)`, `forge-std`. ## Dependencies (tested versions) diff --git a/README.md b/README.md index 2c72439..dcbcbe7 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,6 @@ # DocumentEngine (ERC-1643) > This project has not been audited yet, please use at your own risk. For any questions, please contact [admin@cmta.ch](mailto:admin@cmta.ch). -> -> **Known open items** are tracked in **[`IMPROVEMENT.md`](./IMPROVEMENT.md)**. None is an -> exploitable vulnerability, but integrators should read it before relying on the engine — in -> particular item 2 (a write sent straight to the engine leaves an ERC-1643 subject's own events -> unemitted) and item 1 (one engine instance serves **one trust domain**: `DOCUMENT_MANAGER_ROLE` -> covers every subject, so unrelated issuers should each deploy their own engine rather than share -> one). The `DocumentEngine` is an external contract to manage documents through [*ERC-1643*](https://github.com/ethereum/EIPs/issues/1643), a standard proposition to manage document on-chain. This standard is notably used by [ERC-1400](https://github.com/ethereum/eips/issues/1411) from Polymath. @@ -148,12 +141,13 @@ not honor, enlarging the ABI and inviting integrators to wire it where a real compliance contract is expected. The binding concept we actually need is tiny — "is this caller a token allowed to -manage its own documents?" — so we implement just that: a `TOKEN_CONTRACT_ROLE` -in the role-based `DocumentEngine` (exactly the RuleEngine *binding* mechanism, -which is role-based, not the compliance module) and an owner-managed allowlist in -`DocumentEngineOwnable`. This keeps the engine's surface honest and minimal while -still mirroring the RuleEngine binding pattern. The RuleEngine submodule is kept -as a reference for that pattern. +manage its own documents?" — so we implement just that: a **single allowlist** in +`TokenBindingModule`, shared by both deployments and gated by each one's +document-management hook. It is deliberately **not** a role: there is no +`TOKEN_CONTRACT_ROLE`, and `DocumentEngineOwnable` uses the same allowlist rather +than a separate owner-managed one. This keeps the engine's surface honest and +minimal while still mirroring the RuleEngine binding pattern; the RuleEngine +submodule is kept as a reference for that pattern. ### Events @@ -177,6 +171,51 @@ constructor. To use this engine, a CMTAT token relies on the `DocumentEngineModule` and is wired at runtime with `setDocumentEngine(engine)`; reads/writes are then forwarded to the engine keyed by the token address. +#### Architecture + +One engine serves a whole fleet of tokens. Each token keeps its own document +namespace, keyed by its address, and can never reach another token's: + +![DocumentEngine architecture with CMTAT tokens](./doc/img/cmtat-integration-architecture.png) + +_Diagram source: `doc/img/cmtat-integration-architecture.puml`._ + +#### Wiring and call flow + +Two independent steps wire a token to the engine, and they are easy to get half +right: `bindToken(token)` on the **engine** authorises the token to use the +single-argument ERC-1643 functions, while `setDocumentEngine(engine)` on the +**token** tells it where to forward. Bind without wiring and the token has +nowhere to send; wire without binding and the forwarded call reverts +`NotBoundToken`. + +The diagram below also shows the emission split that makes the pair conformant — +and the one case where it does not hold, the admin path: + +![DocumentEngine and CMTAT call sequence](./doc/img/cmtat-integration-sequence.png) + +_Diagram source: `doc/img/cmtat-integration-sequence.puml`._ + +A minimal integration: + +```solidity +// 1. authorise the token on the engine (engine's document manager) +documentEngine.bindToken(address(token)); + +// 2. point the token at the engine (token's document manager) +token.setDocumentEngine(documentEngine); + +// 3. the token now manages its own documents through the standard ERC-1643 calls, +// and reads are forwarded to the engine keyed by the token address +token.setDocument(bytes32("prospectus"), "ipfs://...", keccak256(bytes(content))); +``` + +Both halves are covered by the test suite against real CMTAT code: +`testCanReturnCMTATDocument` wires `CMTATDocumentEngineMock` (built on CMTAT's +`DocumentEngineModule`) with `setDocumentEngine` and reads through it, and +`testBoundTokenCanManageOwnDocument` exercises the bound-token write and the +namespace isolation that goes with it. + ## Architecture @@ -487,7 +526,8 @@ slither . --checklist --filter-paths "node_modules,lib,test,forge-std,mocks" \ > **Static-analysis output is leads, not findings.** Every dismissal in the feedback files was > verified against the cited `file:line`, and neither tool can see the specification-level issues -> that matter most here — those are in [`IMPROVEMENT.md`](./IMPROVEMENT.md). +> that matter most here — those are tracked under *Known open items* in +> [`AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md). ### Surya diff --git a/doc/audits/AUDIT_OVERVIEW.md b/doc/audits/AUDIT_OVERVIEW.md index 659f69f..8a598f4 100644 --- a/doc/audits/AUDIT_OVERVIEW.md +++ b/doc/audits/AUDIT_OVERVIEW.md @@ -26,7 +26,7 @@ Out of scope: `lib/` (CMTAT, RuleEngine, OpenZeppelin — audited, or not, upstr | --- | --- | --- | --- | | Aderyn `0.6.5` | `v0.4.0` | [report](./tools/v0.4.0/aderyn/aderyn-report.md) | [feedback](./tools/v0.4.0/aderyn/aderyn-report-feedback.md) | | Slither `0.11.5` | `v0.4.0` | [report](./tools/v0.4.0/slither/slither-report.md) | [feedback](./tools/v0.4.0/slither/slither-report-feedback.md) | -| ERC conformance analysis (AI-assisted) | `v0.4.0` | open items: [`IMPROVEMENT.md`](../../IMPROVEMENT.md) | — | +| ERC conformance analysis (AI-assisted) | `v0.4.0` | open items: [below](#known-open-items) | — | | Code-quality review (AI-assisted) | `v0.4.0` | [`CLAUDE_ANALYSIS.md`](./tools/v0.4.0/claude/CLAUDE_ANALYSIS.md) — 14 findings, **no vulnerability**; 6 implemented, 8 deliberately left, nothing outstanding | — | Both tool runs are against CMTAT `v3.3.0-rc3` and OpenZeppelin `v5.7.0`, with mocks and tests @@ -56,7 +56,7 @@ for this engine — those are tracked as open items below. ## Substantive findings fixed in `v0.4.0` -From the ERC conformance analysis (open items: [`IMPROVEMENT.md`](../../IMPROVEMENT.md)) rather than from the +From the ERC conformance analysis (open items: [below](#known-open-items)) rather than from the static analyzers — neither tool can see these, since both are ABI- and specification-level: | Finding | Severity | Status | @@ -69,15 +69,16 @@ static analyzers — neither tool can see these, since both are ABI- and specifi ## Known open items -Not defects in the sense of being exploitable, but tracked deviations from the specifications. Full -detail, with a recommendation for each, in [`IMPROVEMENT.md`](../../IMPROVEMENT.md). +Not defects in the sense of being exploitable, but tracked deviations from the two specifications +this engine implements. **This table is the canonical record**; the items keep the `OPEN-n` numbering +they were first given, which is the numbering the audit reports cite. -| Item | Severity | Where | +| ID | Item | Severity | | --- | --- | --- | -| `_authorizeDocumentManagement()` takes no `subject`, so a deployment cannot make authorization per-subject by overriding the hook. Conformant for the single-issuer fleet the engine targets — `DOCUMENT_MANAGER_ROLE` is permitted to manage every subject — but it means one instance serves one trust domain | Low (Medium if shared across unrelated issuers) | item 1 | -| Admin write path has no execution point in the subject, so an ERC-1643 subject emits nothing for writes sent straight to the engine | Medium | item 2 | -| `_removeDocumentName` is O(n); no paginated enumeration | Low | item 4 — also surfaced by Aderyn L-5 | -| The ERC-2771 trusted forwarder can act as any bound subject and is immutable | Info | item 5 | +| **OPEN-1** | `_authorizeDocumentManagement()` takes no `subject`, so a deployment cannot make authorization per-subject by overriding the hook. Conformant for the single-issuer fleet the engine targets — `DOCUMENT_MANAGER_ROLE` is permitted to manage every subject — but **one engine instance therefore serves one trust domain**: unrelated issuers should each deploy their own rather than share one. | Low (Medium if shared across unrelated issuers) | +| **OPEN-2** | Admin write path has no execution point in the subject, so an ERC-1643 subject emits nothing for a write sent straight to the engine. Point document consumers at the subject only when writes go through the bound-token path. | Medium | +| **OPEN-4** | `_removeDocumentName` is O(n); no paginated enumeration. Also surfaced by Aderyn L-5; the constant was reduced by CLAUDE_ANALYSIS B-1/B-2 but the complexity is unchanged. | Low | +| **OPEN-5** | The ERC-2771 trusted forwarder can act as any bound subject, and is immutable after construction. | Info | ## Reporting a vulnerability diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md index 07aa4a2..35f84f7 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md @@ -19,7 +19,7 @@ operator, a caret pragma, PUSH0, revert-in-loop, storage-writes-in-loop) and one The one result worth keeping in view is **L-5 at `DocumentEngineBase.sol:265`**, which is not a batch loop but the linear scan in `_removeDocumentName`. Aderyn reached it from the "costly operation in a -loop" heuristic; it happens to land on the same code as `IMPROVEMENT.md` item 4, which flags the O(n) +loop" heuristic; it happens to land on the same code as `OPEN-4` in `AUDIT_OVERVIEW.md`, which flags the O(n) removal against the multi-subject draft's expectation of "index tracking to support O(1) removals". That is a scalability item, not a vulnerability — a subject with a large document set makes `removeDocument` progressively more expensive, and `batchRemoveDocuments` compounds it to O(n·m). @@ -31,11 +31,11 @@ CMTAT upgrade. | ID | Detector | Sev | Instances | Disposition | Reason (verified against the cited lines) | | --- | --- | --- | --- | --- | --- | -| L-1 | Centralization Risk | Low | 2 | **By design** | `DocumentEngine.sol:26`, `DocumentEngineOwnable.sol:25`. The whole premise of the contract is that a trusted operator manages documents for a fleet of subjects; `DOCUMENT_MANAGER_ROLE` (and `owner`) are that operator. Documented in the README and analysed in `IMPROVEMENT.md` item 1, which concludes the global role is the correct model for the single-issuer fleet this engine targets. Aderyn cannot express that distinction. | +| L-1 | Centralization Risk | Low | 2 | **By design** | `DocumentEngine.sol:26`, `DocumentEngineOwnable.sol:25`. The whole premise of the contract is that a trusted operator manages documents for a fleet of subjects; `DOCUMENT_MANAGER_ROLE` (and `owner`) are that operator. Documented in the README and analysed as `OPEN-1` in `AUDIT_OVERVIEW.md`, which concludes the global role is the correct model for the single-issuer fleet this engine targets. Aderyn cannot express that distinction. | | L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** | Every file uses a caret pragma, intentionally, so the sources stay consumable as a library by projects on a different `0.8.x`; the compiler actually used for the deployed bytecode is pinned to `0.8.34` in `foundry.toml`, and `foundry.lock` pins every dependency. Verified: no file uses a construct that behaves differently across the allowed range. The floor is now **`^0.8.24`**, raised from `^0.8.20` after the previous run: `^0.8.20` over-promised, because `AccessControlEnumerable.sol` and `EnumerableSet.sol` were already `^0.8.24` and CMTAT `v3.3.0-rc3` moved `draft-IERC1643.sol` there too — no compiler in `0.8.20`–`0.8.23` could build the tree. `0.8.24` is the true `src/` floor; the full project including the CMTAT-importing tests needs `0.8.27`, because `require(cond, CustomError())` is legacy-pipeline-only from that version on. | | L-3 | PUSH0 Opcode | Low | 9 | **Environment** | Consequence of the caret pragma plus `evm_version = prague`: the compiler emits `PUSH0`, which is unavailable on chains that have not adopted Shanghai. Not a source defect. A deployer targeting such a chain must lower `evm_version` in `foundry.toml` — but CMTAT v3 itself requires `prague`, so that configuration is out of scope for this engine. | | L-4 | Loop Contains `require`/`revert` | Low | 4 | **By design** | `DocumentEngineBase.sol:118, 141, 158, 175` — the four batch loops. The reverts are raised inside `_setDocument` / `_removeDocument` (`ERC1643InvalidName`, `MultiDocumentInvalidSubject`, `ERC1643MissingDocument`). Batch operations are deliberately **all-or-nothing**: a batch containing one bad entry must not half-apply, since partial application would leave the operator unable to tell which documents were written without re-reading every entry. Skipping bad entries instead would silently drop them. | -| L-5 | Costly operations inside loop | Low | 5 | **By design** ×4, **known item** ×1 | Four instances (`:118, 141, 158, 175`) are storage writes in the batch loops — unavoidable, and the reason the batch functions exist is to amortise the 21 000-gas transaction overhead across those writes. The fifth (`:265`) is `_removeDocumentName`'s linear scan with swap-and-pop; see the triage note above and `IMPROVEMENT.md` item 4. | +| L-5 | Costly operations inside loop | Low | 5 | **By design** ×4, **known item** ×1 | Four instances (`:118, 141, 158, 175`) are storage writes in the batch loops — unavoidable, and the reason the batch functions exist is to amortise the 21 000-gas transaction overhead across those writes. The fifth (`:265`) is `_removeDocumentName`'s linear scan with swap-and-pop; see the triage note above and `OPEN-4` in `AUDIT_OVERVIEW.md`. | | L-6 | Unchecked Return | Low | 1 | **False positive** | `DocumentEngine.sol:43`, `_grantRole(DEFAULT_ADMIN_ROLE, admin);`. OpenZeppelin's `_grantRole` returns `false` only when the account already holds the role. This call is in the constructor of a freshly deployed contract, where no role has been granted yet, so it always returns `true`; `admin == address(0)` is already rejected on the preceding lines. There is no state to check and no recovery path to take. | ## Delta diff --git a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md index cd09669..767b782 100644 --- a/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md +++ b/doc/audits/tools/v0.4.0/aderyn/aderyn-report.md @@ -6,7 +6,7 @@ > | Tool version | `aderyn 0.6.5` | > | Scope | `src/` only — 9 files, 307 nSLOC. **Mocks/tests excluded** (this project has no `src/mocks`; its mocks live in `test/`, which Aderyn does not scan). | > | Result | **0 High · 6 Low · 0 Info** | -> | Verdict | **Nothing to fix.** No finding is exploitable. One (L-5 at `DocumentEngineBase.sol:238`) independently corroborates a known gas/scalability item already tracked as `IMPROVEMENT.md` item 4. | +> | Verdict | **Nothing to fix.** No finding is exploitable. One (L-5 at `DocumentEngineBase.sol:265`) independently corroborates a known gas/scalability item already tracked as `OPEN-4` in `AUDIT_OVERVIEW.md`. | > > | ID | Detector | Sev | Instances | Assessment | > | --- | --- | --- | --- | --- | @@ -14,7 +14,7 @@ > | L-2 | Unspecific Solidity Pragma | Low | 9 | **By design** — the caret is deliberate; the deployed compiler is pinned in `foundry.toml`. Now `^0.8.24`, the true `src/` floor | > | L-3 | PUSH0 Opcode | Low | 9 | **Environment** — `evm_version = prague`; only relevant on chains without PUSH0 | > | L-4 | Loop Contains `require`/`revert` | Low | 4 | **By design** — batch operations are deliberately all-or-nothing | -> | L-5 | Costly operations inside loop | Low | 5 | **By design** (4 batch loops) + **1 known item** — `_removeDocumentName` is O(n), see `IMPROVEMENT.md` item 4 | +> | L-5 | Costly operations inside loop | Low | 5 | **By design** (4 batch loops) + **1 known item** — `_removeDocumentName` is O(n), see `OPEN-4` in `AUDIT_OVERVIEW.md` | > | L-6 | Unchecked Return | Low | 1 | **False positive** — `_grantRole` in a constructor on a fresh contract cannot return `false` | > > Re-run after the code-quality review (`../claude/CLAUDE_ANALYSIS.md`). **Unchanged**: same six diff --git a/doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md b/doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md index ea9fec0..6aa8cd8 100644 --- a/doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md +++ b/doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md @@ -135,7 +135,7 @@ Measured on a subject holding 20 documents, toggled in place, same harness both | target at index 19 (full scan) | 87 250 | 85 050 | **−2200** | | target at index 0 (early exit) | 28 397 | 28 002 | −395 | -≈116 gas per iteration. This is the function `IMPROVEMENT.md` item 4 already flags as the O(n) +≈116 gas per iteration. This is the function `OPEN-4` in `AUDIT_OVERVIEW.md` already flags as the O(n) scalability hotspot, and `batchRemoveDocuments` compounds it to O(n·m), so the saving multiplies. **Verdict: implemented.** Note this does not change the complexity — it lowers the constant. The @@ -204,7 +204,7 @@ and a future refactor should preserve it. `DocumentEngine.sol:39` / `DocumentEngineOwnable.sol:31` pass `forwarderIrrevocable` to `ERC2771Context` and emit nothing, so a log-only indexer never sees the value. That matters more than -usual here because the forwarder can act as any bound subject (`IMPROVEMENT.md` item 5). +usual here because the forwarder can act as any bound subject (`OPEN-5` in `AUDIT_OVERVIEW.md`). Against that: the value is `immutable`, so it can never change and there is no sequence to reconstruct; it is publicly readable — `trustedForwarder()` (`0x7da0a877`) and diff --git a/doc/img/cmtat-integration-architecture.png b/doc/img/cmtat-integration-architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..cdc908678a6891727fc39efc778872566ea60f78 GIT binary patch literal 40084 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 YE?KU~`>9uRXsAn3Rz;><$~fSE08Jb0WdHyG literal 0 HcmV?d00001 diff --git a/doc/img/cmtat-integration-architecture.puml b/doc/img/cmtat-integration-architecture.puml new file mode 100644 index 0000000..e181b7b --- /dev/null +++ b/doc/img/cmtat-integration-architecture.puml @@ -0,0 +1,74 @@ +@startuml +title DocumentEngine — architecture with CMTAT tokens\nOne engine stores documents for many subjects + +skinparam componentStyle rectangle +skinparam shadowing false +skinparam defaultTextAlignment center +skinparam linetype ortho +skinparam component { + BackgroundColor #FDFDFD + BorderColor #666666 +} +skinparam database { + BackgroundColor #EEF5FF + BorderColor #666666 +} + +package "Token side — one contract per issuance" as TokenSide { + component "CMTAT token A\n(subject)" as TokenA + component "CMTAT token B\n(subject)" as TokenB +} + +package "Engine side — one deployment serves the whole fleet" as EngineSide { + component "**DocumentEngine**\nAccessControlEnumerable" as Engine + component "**DocumentEngineOwnable**\nOwnable2Step" as EngineOwnable + + package "shared modules" as Modules { + component "DocumentEngineBase\ndocument logic + storage" as Base + component "TokenBindingModule\nbinding allowlist" as Binding + component "VersionModule\nERC-8303 version()" as Version + } + + database "**_documents**\n[subject][name] -> Document\n**_documentNames**\n[subject] -> bytes32[]" as Store +} + +actor "Document manager" as Operator + +Engine -[hidden]right- EngineOwnable + +TokenA --> Engine : **bound-token path**\nsubject = msg.sender +TokenB --> Engine + +Operator --> Engine : **admin path**\nsubject is an argument + +Engine --|> Base +Engine --|> Binding +Engine --|> Version +EngineOwnable --|> Base +EngineOwnable --|> Binding +EngineOwnable --|> Version + +Base --> Store : reads / writes\nkeyed by subject + +note bottom of TokenSide + Each token embeds CMTAT's **DocumentEngineModule**: + setDocumentEngine(engine) once, then setDocument / + removeDocument / getDocument are **forwarded** to the + engine. The token re-emits DocumentUpdated and + DocumentRemoved on its **own** address. +end note + +note right of Binding + A token may use the single-argument + ERC-1643 functions only while bound. + Not a role — one allowlist, shared + by both deployments. +end note + +note right of Store + A subject can only ever touch its own + namespace: the bound-token path passes + _msgSender() as the subject. +end note + +@enduml diff --git a/doc/img/cmtat-integration-sequence.png b/doc/img/cmtat-integration-sequence.png new file mode 100644 index 0000000000000000000000000000000000000000..543df0c75dad4a78208207276a2f49db26352129 GIT binary patch literal 92021 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

;_zF%C|RXMc>*{N8ob@19wa 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

fmoj&Ke9eAa#$SKtdX8z`{%(sbs)o#5kTd^x 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" as Token +participant "DocumentEngine\nDocumentEngineBase\n+ TokenBindingModule" as Engine + +== 1. Wiring (once) == + +Operator -> Engine : bindToken(token) +activate Engine +Engine -> Engine : _authorizeDocumentManagement() +Engine -> Engine : _setTokenBinding(token, true) +Engine --> Operator : emit **TokenBindingSet**(token, true)\non the engine — only on a real change +deactivate Engine + +TokenAdmin -> Token : setDocumentEngine(engine) +activate Token +Token --> TokenAdmin : emit **DocumentEngine**(engine)\non the token +deactivate Token + +note over Token, Engine + Both steps are required and are independent. + Binding without setDocumentEngine → the token has nowhere to forward to. + setDocumentEngine without binding → the forwarded call reverts **NotBoundToken**. +end note + +== 2. Bound-token write — the path integrators use == + +TokenAdmin -> Token : setDocument(name, uri, hash) +activate Token +Token -> Token : _authorizeDocumentManagement()\ntoken-side access control + +alt no engine configured + Token --> TokenAdmin : revert **CMTAT_DocumentEngineModule_NoDocumentEngine** +else engine configured + Token -> Engine : setDocument(name, uri, hash)\nmsg.sender = token + activate Engine + Engine -> Engine : _checkTokenBound() + + alt token not bound + Engine --> Token : revert **NotBoundToken**(token) + else name == 0 + Engine --> Token : revert **ERC1643InvalidName**() + else accepted + Engine -> Engine : _setDocument(subject = _msgSender(), ...)\nwrites _documents[token][name] + Engine --> Engine : emit **DocumentUpdatedForSubject**(token, name, uri, hash)\non the ENGINE — carries the subject address + end + deactivate Engine + + Token --> TokenAdmin : emit **DocumentUpdated**(name, uri, hash)\non the TOKEN — the address ERC-1643 consumers watch +end +deactivate Token + +note over Token, Engine #EEF5FF + **Emission responsibility.** The engine is a shared multi-subject manager, so it emits + only the address-carrying `*ForSubject` events — the base ERC-1643 events carry no + address and could not say which subject changed. The token re-emits the base event on + its own address. Together the pair is conformant. +end note + +== 3. Admin write — same storage, one event short == + +Operator -> Engine : setDocument(**subject**, name, uri, hash) +activate Engine +Engine -> Engine : _authorizeDocumentManagement() +Engine -> Engine : _setDocument(subject, ...) +Engine --> Operator : emit **DocumentUpdatedForSubject**(subject, ...) +deactivate Engine + +note over Token, Engine #FDEEEE + The subject is **never called**, so it emits nothing: anyone subscribed to the token's + address sees no change. Tracked as **OPEN-2** in AUDIT_OVERVIEW.md. Use the bound-token + path when consumers watch the token; use the admin path for bulk/backfill operations. +end note + +== 4. Read == + +participant "Consumer" as Consumer +Consumer -> Token : getDocument(name) +activate Token +Token -> Engine : getDocument(name)\nmsg.sender = token +Engine --> Token : (uri, hash, lastModified)\nfrom _documents[token][name] +Token --> Consumer : (uri, hash, lastModified) +deactivate Token + +note over Consumer, Engine + Read through the **token**, or use the engine's address-scoped + getDocument(subject, name). Calling the engine's single-argument + getDocument(name) directly reads the **caller's own** namespace — + empty, with no revert. +end note + +@enduml From 77b9bd364a8fa66d94f4083d8dc079b49a3a1142 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:12:54 +0200 Subject: [PATCH 38/47] docs: split README into a short entry point and doc/DOCUMENTATION.md --- AGENTS.md | 8 +- CHANGELOG.md | 4 +- CLAUDE.md | 8 +- README.md | 690 +++++-------------------------------------- doc/DOCUMENTATION.md | 670 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 757 insertions(+), 623 deletions(-) create mode 100644 doc/DOCUMENTATION.md diff --git a/AGENTS.md b/AGENTS.md index 36fa1e4..5da3f24 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -159,7 +159,11 @@ Other important files: - `remappings.txt` — `CMTAT/`, `RuleEngine/`, `OZ/`, `@openzeppelin/contracts-upgradeable/`. - `CHANGELOG.md` — semver history; update on every release (current: `v0.4.0`). - `ERC-1643-proposition.md` — proposed optional multi-token events / extension. -- `README.md` — full documentation, Surya schema, and the CMTAT integration diagrams. +- `README.md` — **short** entry point only: what the engine is, quick start, the two management + paths, the CMTAT wiring, the two integrator caveats, deploy. Keep it short; new prose belongs in + the full document. +- `doc/DOCUMENTATION.md` — the full documentation (Surya schema, ERC-165 rationale, version + compatibility matrix, tooling). This is where the old root README moved. - `doc/img/` — PlantUML **sources** (`*.puml`) plus their rendered `*.png`. The README embeds only the images. Re-render with `plantuml -tpng doc/img/.puml` after editing a source, and look at the PNG: PlantUML draws syntax/deprecation warnings *into* the image and still exits 0. @@ -167,7 +171,7 @@ Other important files: `.sol` in `src/` (9 each), regenerated by the three scripts in `doc/script/` — run them from that directory, **graph first** (it creates the scratch `docOut/`; the report script's `mkdir` lacks `-p`). Patch `surya/lib/graph.js` before regenerating or every contract calling `super.()` - yields a silent 0-byte PNG; see the Surya section in `README.md`. Also coverage, and + yields a silent 0-byte PNG; see the Surya section in `doc/DOCUMENTATION.md`. Also coverage, and `doc/audits/` — the security overview (`AUDIT_OVERVIEW.md`) plus versioned static-analysis output under `doc/audits/tools/vX.Y.Z//`, each with a `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging diff --git a/CHANGELOG.md b/CHANGELOG.md index bffcf5b..ae1b63e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,7 +47,7 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) ## v0.4.0 -Targets **CMTAT `v3.3.0-rc3`** — see the [compatibility matrix](./README.md#version-compatibility) +Targets **CMTAT `v3.3.0-rc3`** — see the [compatibility matrix](./doc/DOCUMENTATION.md#version-compatibility) for which CMTAT release each version of this engine is built against. > **Versioning note.** `getDocument` changes shape relative to `v0.3.0`, which the convention above @@ -74,7 +74,7 @@ for which CMTAT release each version of this engine is built against. (8436 bytes; only the CBOR metadata trailer moves, because the source text of `AccessControlEnumerable.sol` changed), and `DocumentEngineOwnable`'s bytecode is unchanged including metadata. - - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) [`v3.0.0-rc5`](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) + - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) [`v3.0.0-rc5`](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./doc/DOCUMENTATION.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) - `foundry.lock` now records every submodule by tag; all five entries had gone stale since `v0.3.0`. - **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) - **Code-quality review** (`doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md`) — 14 findings, none a vulnerability. diff --git a/CLAUDE.md b/CLAUDE.md index 36fa1e4..5da3f24 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -159,7 +159,11 @@ Other important files: - `remappings.txt` — `CMTAT/`, `RuleEngine/`, `OZ/`, `@openzeppelin/contracts-upgradeable/`. - `CHANGELOG.md` — semver history; update on every release (current: `v0.4.0`). - `ERC-1643-proposition.md` — proposed optional multi-token events / extension. -- `README.md` — full documentation, Surya schema, and the CMTAT integration diagrams. +- `README.md` — **short** entry point only: what the engine is, quick start, the two management + paths, the CMTAT wiring, the two integrator caveats, deploy. Keep it short; new prose belongs in + the full document. +- `doc/DOCUMENTATION.md` — the full documentation (Surya schema, ERC-165 rationale, version + compatibility matrix, tooling). This is where the old root README moved. - `doc/img/` — PlantUML **sources** (`*.puml`) plus their rendered `*.png`. The README embeds only the images. Re-render with `plantuml -tpng doc/img/.puml` after editing a source, and look at the PNG: PlantUML draws syntax/deprecation warnings *into* the image and still exits 0. @@ -167,7 +171,7 @@ Other important files: `.sol` in `src/` (9 each), regenerated by the three scripts in `doc/script/` — run them from that directory, **graph first** (it creates the scratch `docOut/`; the report script's `mkdir` lacks `-p`). Patch `surya/lib/graph.js` before regenerating or every contract calling `super.()` - yields a silent 0-byte PNG; see the Surya section in `README.md`. Also coverage, and + yields a silent 0-byte PNG; see the Surya section in `doc/DOCUMENTATION.md`. Also coverage, and `doc/audits/` — the security overview (`AUDIT_OVERVIEW.md`) plus versioned static-analysis output under `doc/audits/tools/vX.Y.Z//`, each with a `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging diff --git a/README.md b/README.md index dcbcbe7..3da310d 100644 --- a/README.md +++ b/README.md @@ -1,666 +1,122 @@ -# DocumentEngine (ERC-1643) +# DocumentEngine (ERC-1643) > This project has not been audited yet, please use at your own risk. For any questions, please contact [admin@cmta.ch](mailto:admin@cmta.ch). -The `DocumentEngine` is an external contract to manage documents through [*ERC-1643*](https://github.com/ethereum/EIPs/issues/1643), a standard proposition to manage document on-chain. This standard is notably used by [ERC-1400](https://github.com/ethereum/eips/issues/1411) from Polymath. +A standalone contract that stores **[ERC-1643](https://github.com/ethereum/EIPs/issues/1643) +documents on-chain on behalf of other contracts** — typically [CMTAT](https://github.com/CMTA/CMTAT) +tokens. One engine serves a whole fleet: each subject gets its own namespace, keyed by its address, +and can never reach another's. -The documentEngine is planned to be used by other smart contract,e.g CMTAT token, to store documents on their behalf. +A document is `{ string uri, bytes32 documentHash, uint256 lastModified }`, addressed by a `bytes32` +name. -The ERC-1643 defines a document with three attributes: +Why use an external engine rather than storing documents in the token: -- A short name (represented as a `bytes32`) -- A generic URI (represented as a `string`) that could point to a website or other document portal. -- The hash of the document contents associated with it on-chain. +- keeps the token's bytecode small; +- lets one operator manage documents for many tokens; +- documents can be updated without touching the token. -A smart contract needs only to read documents from this standard through the interface [IERC1643](./lib/CMTAT/contracts/interfaces/tokenization/draft-IERC1643.sol) to get the documents from the documentEngine: +**Full documentation: [`doc/DOCUMENTATION.md`](./doc/DOCUMENTATION.md).** -```solidity -interface IERC1643 { - error ERC1643InvalidName(); - error ERC1643MissingDocument(); - - function getDocument(bytes32 name) - external - view - returns (string memory uri, bytes32 documentHash, uint256 lastModified); - function getAllDocuments() external view returns (bytes32[] memory documentNames_); - function setDocument(bytes32 name, string calldata uri, bytes32 documentHash) external; - function removeDocument(bytes32 name) external; -} -``` - -> **Note — `getDocument` returns flat values.** CMTAT `v3.3.0-rc1` briefly returned a `Document` -> struct here; `v3.3.0-rc2` restored the three flat return values mandated by the ERC-1643 ABI, and -> this engine follows. The distinction matters because return types are not part of a function -> signature: both shapes have the same selector and the same `type(IERC1643).interfaceId`, so a -> struct return is undetectable through ERC-165 and a consumer built from the specification ABI -> would silently decode it as garbage. The `Document` struct is kept internally for storage only. -> `testGetDocumentReturnsFlatErc1643Abi` pins the wire format. - -Using an external contract for your smart contract provides two advantages: - -- Reduce code size of your smart contract -- Allow to manage documents for several different smart contracts - -### Two ways to manage documents - -The engine supports **two management paths** at the same time: - -**1. Admin path (`DOCUMENT_MANAGER_ROLE`).** Since the engine manages documents -for several different smart contracts, the admin functions take one supplementary -`address smartContract` argument compared to the ERC-1643: - -```solidity -// DocumentEngine (admin overloads) -function setDocument(address smartContract, bytes32 name_, string memory uri_, bytes32 documentHash_) external; -function removeDocument(address smartContract, bytes32 name_) external; -``` - -**2. Bound-token path.** This implements the standard, single-argument ERC-1643 -functions. A token is *bound* to the engine through the shared **`ITokenBinding`** -surface — identical across both deployments, so integrators bind/query a token the -same way regardless of the access-control model: +## Quick start -```solidity -documentEngine.bindToken(address(token)); // also: unbindToken(token), isTokenBound(token) -``` - -Both deployments share the exact same binding mechanism — a single allowlist in -`TokenBindingModule` (`src/modules/TokenBindingModule.sol`), **not** a role. They -expose the same `bindToken` / `unbindToken` / `isTokenBound` functions, emit the -same `TokenBindingSet` event, and revert with the same `NotBoundToken` error when a -non-bound caller attempts a write. The only difference is *who* may bind: whoever -may manage documents in that deployment (the `DOCUMENT_MANAGER_ROLE` holder, or the -`owner`), since binding is authorized by the same document-management hook. - -Once bound, the token manages its **own** documents (`msg.sender` is the token); -it can never affect another contract's documents: - -```solidity -// DocumentEngine (standard ERC-1643, scoped to msg.sender) -function setDocument(bytes32 name_, string calldata uri_, bytes32 documentHash_) external; -function removeDocument(bytes32 name_) external; +```bash +git clone --recurse-submodules https://github.com/CMTA/DocumentEngine +cd DocumentEngine +forge build +forge test ``` -> This mirrors the RuleEngine *binding* pattern without reusing its -> `ERC3643ComplianceExtendedModule` — see -> [Why not reuse RuleEngine's ERC-3643 compliance module?](#why-not-reuse-ruleengines-erc-3643-compliance-module) below. +Requires [Foundry](https://getfoundry.sh) and Solidity `0.8.34` (`evm_version = prague`). Sources +declare `pragma ^0.8.24`; building the tests needs `≥ 0.8.27`. -### Flexible access control +## Two ways to manage documents -Following the CMTAT / [RuleEngine](https://github.com/CMTA/RuleEngine) pattern, -the restricted functions do not hardcode a check. They carry a **modifier** -(`onlyDocumentManager` / `onlyBoundToken`) that delegates to an **overridable -`internal virtual` authorization hook**: +Both are active at once. -- the **admin path** delegates to `_authorizeDocumentManagement()`, the one hook - each deployment implements (`_checkRole(DOCUMENT_MANAGER_ROLE)` for - `DocumentEngine`, `_checkOwner()` for `DocumentEngineOwnable`); -- the **bound-token path** delegates to `_authorizeBoundTokenDocumentManagement()`, - which `TokenBindingModule` implements once for both deployments (it checks the - shared binding allowlist). +**Admin path** — a document manager writes for *any* subject, passing the address explicitly: ```solidity -// implemented per deployment (the only access-control hook they supply) -function _authorizeDocumentManagement() internal view virtual { - _checkRole(DOCUMENT_MANAGER_ROLE); // or _checkOwner() -} - -// implemented once in TokenBindingModule for both deployments -function _authorizeBoundTokenDocumentManagement() internal view virtual override { - _checkTokenBound(); // reverts NotBoundToken if msg.sender is not bound -} +documentEngine.setDocument(address(token), name, uri, documentHash); +documentEngine.removeDocument(address(token), name); ``` -This separates the document-management implementation from the authorization -logic: a subclass changes *who* is authorized by overriding the hook, never by -touching the management functions. - -### Why not reuse RuleEngine's ERC-3643 compliance module? - -CMTA's [RuleEngine](https://github.com/CMTA/RuleEngine) (v3) ships an -`ERC3643ComplianceExtendedModule` that offers a ready-made token-binding registry -(`bindToken` / `unbindToken` / `isTokenBound` / `getTokenBounds`). It is tempting -to reuse it for the bound-token path, but we deliberately do **not**, because that -module is an **`IERC3643Compliance`** — a *transfer-compliance* contract. - -Inheriting it would force the DocumentEngine to also implement the ERC-3643 -transfer-compliance callbacks that come with that interface: +**Bound-token path** — a *bound* token manages its **own** documents through the standard +single-argument ERC-1643 functions (`msg.sender` is the subject): ```solidity -function canTransfer(address, address, uint256) external view returns (bool); -function transferred(address, address, uint256) external; -function created(address, uint256) external; -function destroyed(address, uint256) external; +documentEngine.bindToken(address(token)); // once, by the document manager +// then, called by the token itself: +documentEngine.setDocument(name, uri, documentHash); ``` -A document engine has **nothing to do with token transfers**, so these would have -to be stubbed as no-ops (`canTransfer` always returning `true`). That is -misleading: the contract would advertise a transfer-compliance surface it does -not honor, enlarging the ABI and inviting integrators to wire it where a real -compliance contract is expected. - -The binding concept we actually need is tiny — "is this caller a token allowed to -manage its own documents?" — so we implement just that: a **single allowlist** in -`TokenBindingModule`, shared by both deployments and gated by each one's -document-management hook. It is deliberately **not** a role: there is no -`TOKEN_CONTRACT_ROLE`, and `DocumentEngineOwnable` uses the same allowlist rather -than a separate owner-managed one. This keeps the engine's surface honest and -minimal while still mirroring the RuleEngine binding pattern; the RuleEngine -submodule is kept as a reference for that pattern. - -### Events +Binding is a single allowlist shared by both deployments — **not** a role. -This engine is a **shared, multi-token** document manager, so — per the ERC-1643 -["Emission Responsibility"](./doc/ERCSpecification/erc-1643.md) rules — it emits -**only** the address-carrying extension events -`DocumentUpdatedForSubject(address indexed subject, …)` / -`DocumentRemovedForSubject(…)`, and **not** the base `DocumentUpdated` / -`DocumentRemoved` events. The base events carry no address and so cannot identify -which token contract a change belongs to; they are the responsibility of the -token contract that exposes ERC-1643 to consumers (it re-emits them when -delegating). See -the [Multi-Subject Document Management draft](./doc/ERCSpecification/erc-draft_multi_document_management.md) -and the `IERC1643MultiDocument` extension. +## Two deployments -### Integration with CMTAT - -Since CMTAT v3, the shipped standalone tokens store documents on-chain -(`DocumentERC1643Module`) and do not consume an external engine through their -constructor. To use this engine, a CMTAT token relies on the -`DocumentEngineModule` and is wired at runtime with `setDocumentEngine(engine)`; -reads/writes are then forwarded to the engine keyed by the token address. - -#### Architecture - -One engine serves a whole fleet of tokens. Each token keeps its own document -namespace, keyed by its address, and can never reach another token's: - -![DocumentEngine architecture with CMTAT tokens](./doc/img/cmtat-integration-architecture.png) - -_Diagram source: `doc/img/cmtat-integration-architecture.puml`._ - -#### Wiring and call flow - -Two independent steps wire a token to the engine, and they are easy to get half -right: `bindToken(token)` on the **engine** authorises the token to use the -single-argument ERC-1643 functions, while `setDocumentEngine(engine)` on the -**token** tells it where to forward. Bind without wiring and the token has -nowhere to send; wire without binding and the forwarded call reverts -`NotBoundToken`. - -The diagram below also shows the emission split that makes the pair conformant — -and the one case where it does not hold, the admin path: +| Contract | Access control | Document management + binding restricted to | +| --- | --- | --- | +| `DocumentEngine` | `AccessControlEnumerable` | `DOCUMENT_MANAGER_ROLE` | +| `DocumentEngineOwnable` | `Ownable2Step` | `owner` | -![DocumentEngine and CMTAT call sequence](./doc/img/cmtat-integration-sequence.png) +They share all the logic (`DocumentEngineBase`, `TokenBindingModule`, `VersionModule`) and differ +only in *who* is authorized. Authorization goes through an overridable `internal virtual` hook, so a +subclass changes who may write without touching the management functions. -_Diagram source: `doc/img/cmtat-integration-sequence.puml`._ +## Using it with a CMTAT token -A minimal integration: +Two independent steps, and it is easy to do only one: `bindToken` on the **engine** authorises the +token, `setDocumentEngine` on the **token** tells it where to forward. Bind without wiring and the +token has nowhere to send; wire without binding and the forwarded call reverts `NotBoundToken`. ```solidity -// 1. authorise the token on the engine (engine's document manager) -documentEngine.bindToken(address(token)); - -// 2. point the token at the engine (token's document manager) -token.setDocumentEngine(documentEngine); +documentEngine.bindToken(address(token)); // engine's document manager +token.setDocumentEngine(documentEngine); // token's document manager -// 3. the token now manages its own documents through the standard ERC-1643 calls, -// and reads are forwarded to the engine keyed by the token address token.setDocument(bytes32("prospectus"), "ipfs://...", keccak256(bytes(content))); ``` -Both halves are covered by the test suite against real CMTAT code: -`testCanReturnCMTATDocument` wires `CMTATDocumentEngineMock` (built on CMTAT's -`DocumentEngineModule`) with `setDocumentEngine` and reads through it, and -`testBoundTokenCanManageOwnDocument` exercises the bound-token write and the -namespace isolation that goes with it. - - - -## Architecture - -The engine is split into two contracts (CMTAT module/deployment pattern): - -- **`DocumentEngineBase`** (abstract) — holds the document storage and all the - ERC-1643 document-management functions, plus the `onlyDocumentManager` / - `onlyBoundToken` modifiers and the **abstract** `_authorize*` hooks. It is - agnostic to the access-control implementation. -- **`DocumentEngine`** (deployment) — the concrete, deployable contract. It - defines the **access control** (`AccessControlEnumerable`, the `_authorize*` - hook implementations and the `hasRole` override) and wires the ERC-2771 - (gasless) support. `AccessControlEnumerable` additionally allows enumerating - the members of each role on-chain. -- **`DocumentEngineOwnable`** (alternative deployment) — same base logic, but - access control is a single **owner** via `Ownable2Step` (two-step ownership - transfer) instead of roles. Both document management and token binding are - `owner`-only. -- **`TokenBindingModule`** (`src/modules/TokenBindingModule.sol`) — the shared - token-binding registry (an allowlist) implementing `ITokenBinding` - (`bindToken` / `unbindToken` / `isTokenBound` + `TokenBindingSet`). Both - deployments inherit it, so binding is identical (same functions, event, and - `NotBoundToken` revert) and ERC-165-discoverable regardless of the - access-control model; binding is authorized by each deployment's - document-management hook. - -`DocumentEngineInvariant` provides the errors shared by every deployment. -Access-control specifics are **not** defined there: the `DOCUMENT_MANAGER_ROLE` -constant lives in the role-based `DocumentEngine`, and the owner logic in -`DocumentEngineOwnable`. - -`VersionModule` (`src/modules/VersionModule.sol`) isolates the version concern -and implements [ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) -(see below). - -## Version (ERC-8303) - -The contract version is exposed through the `VersionModule`, which implements -the [ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) -`IERC8303` interface: - -```solidity -interface IERC8303 { - function version() external view returns (string memory); -} -``` - -- `version()` returns the current version string (e.g. `"0.4.0"`), following - Semantic Versioning 2.0.0. -- The public `VERSION` constant is kept for backward compatibility and returns - the same value. -- ERC-165 discovery is supported: `supportsInterface(0x54fd4d50)` (the ERC-8303 - interface id) returns `true`. - -### ERC-165: what the engine advertises - -Both deployments advertise: - -| Interface | Id | | -| --- | --- | --- | -| `IERC1643` | `0xecfecec8` | base single-argument functions, for a **bound subject** | -| `IERC1643MultiDocument` | `0xa2b1179b` | address-scoped document management | -| `ITokenBinding` | — | `bindToken` / `unbindToken` / `isTokenBound` | -| `IERC8303` | `0x54fd4d50` | `version()` | -| `IERC165` | `0x01ffc9a7` | | -| `IAccessControlEnumerable` | — | `DocumentEngine` only | - -`type(IERC1643).interfaceId` is advertised because the engine really does implement the base -single-argument functions. Its audience is a **token wiring itself to the engine**: before calling -`setDocumentEngine(engine)`, or before forwarding `setDocument(name, uri, hash)`, a token can confirm -through ERC-165 that those endpoints exist here rather than discovering it from a failed call. -`ITokenBinding` answers the complementary question — does this engine have a binding surface — and -`isTokenBound(address(this))` whether that particular token may use it. - -> **It is not an invitation to read documents from this address.** The base functions are -> `_msgSender()`-scoped, so a third party calling `getDocument(name)` on the engine reads *its own*, -> empty namespace — no revert, no error, just nothing — and the engine emits only the -> address-carrying `*ForSubject` events. Point document consumers at the **subject**, or use the -> address-scoped `getDocument(subject, name)`. Asserted by -> `testBaseERC1643IsAdvertisedButReadsAreCallerScoped`. - -## Schema - -Generated with Surya — regenerate with the three scripts in [`doc/script`](./doc/script). Diagrams -for **every** file in `src/`, interfaces included, live under [`doc/surya`](./doc/surya); the ones -below are the two deployments and the base they share. - -### Inheritance - -Both deployments sit on the same two modules — `DocumentEngineBase` (document logic) and -`TokenBindingModule` (the binding allowlist) — and differ only in the access-control layer. - -#### `DocumentEngine` — role-based (`AccessControlEnumerable`) - -![surya_inheritance_DocumentEngine.sol](./doc/surya/surya_inheritance/surya_inheritance_DocumentEngine.sol.png) - -#### `DocumentEngineOwnable` — single owner (`Ownable2Step`) - -![surya_inheritance_DocumentEngineOwnable.sol](./doc/surya/surya_inheritance/surya_inheritance_DocumentEngineOwnable.sol.png) - -### Graph - -#### `DocumentEngineBase` — the shared document logic - -![surya_graph_DocumentEngineBase.sol](./doc/surya/surya_graph/surya_graph_DocumentEngineBase.sol.png) - -#### `DocumentEngine` - -![surya_graph_DocumentEngine.sol](./doc/surya/surya_graph/surya_graph_DocumentEngine.sol.png) - -#### `DocumentEngineOwnable` - -![surya_graph_DocumentEngineOwnable.sol](./doc/surya/surya_graph/surya_graph_DocumentEngineOwnable.sol.png) - -## Surya Description Report - -### Contracts Description Table - -Per-file reports live in [`doc/surya/surya_report`](./doc/surya/surya_report); the tables below merge -them. Note that the document functions belong to **`DocumentEngineBase`**, not to either deployment — -each deployment contributes only its access-control layer and its ERC-2771 context overrides. - -| Contract | Type | Bases | | | -|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| -| └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | -|||||| -| **DocumentEngineBase** | Implementation | IERC1643, IERC1643MultiDocument, DocumentEngineInvariant, Context ||| -| └ | removeDocument | External ❗️ | 🛑 | onlyDocumentManager | -| └ | setDocument | External ❗️ | 🛑 | onlyBoundToken | -| └ | removeDocument | External ❗️ | 🛑 | onlyBoundToken | -| └ | batchSetDocuments | External ❗️ | 🛑 | onlyDocumentManager | -| └ | batchSetDocuments | External ❗️ | 🛑 | onlyDocumentManager | -| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyDocumentManager | -| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyDocumentManager | -| └ | getDocument | External ❗️ | |NO❗️ | -| └ | getDocument | External ❗️ | |NO❗️ | -| └ | getAllDocuments | External ❗️ | |NO❗️ | -| └ | getAllDocuments | External ❗️ | |NO❗️ | -| └ | setDocument | Public ❗️ | 🛑 | onlyDocumentManager | -| └ | _removeDocumentName | Internal 🔒 | 🛑 | | -| └ | _removeDocument | Internal 🔒 | 🛑 | | -| └ | _setDocument | Internal 🔒 | 🛑 | | -| └ | _authorizeDocumentManagement | Internal 🔒 | | | -| └ | _authorizeBoundTokenDocumentManagement | Internal 🔒 | | | -| └ | _getDocument | Internal 🔒 | | | -|||||| -| **DocumentEngine** | Implementation | TokenBindingModule, VersionModule, AccessControlEnumerable, ERC2771Context ||| -| └ | | Public ❗️ | 🛑 | ERC2771Context | -| └ | hasRole | Public ❗️ | |NO❗️ | -| └ | supportsInterface | Public ❗️ | |NO❗️ | -| └ | _authorizeDocumentManagement | Internal 🔒 | | | -| └ | _msgSender | Internal 🔒 | | | -| └ | _msgData | Internal 🔒 | | | -| └ | _contextSuffixLength | Internal 🔒 | | | -|||||| -| **DocumentEngineOwnable** | Implementation | TokenBindingModule, VersionModule, Ownable2Step, ERC2771Context ||| -| └ | | Public ❗️ | 🛑 | Ownable ERC2771Context | -| └ | supportsInterface | Public ❗️ | |NO❗️ | -| └ | _authorizeDocumentManagement | Internal 🔒 | | | -| └ | _msgSender | Internal 🔒 | | | -| └ | _msgData | Internal 🔒 | | | -| └ | _contextSuffixLength | Internal 🔒 | | | -|||||| -| **TokenBindingModule** | Implementation | DocumentEngineBase, ITokenBinding ||| -| └ | bindToken | External ❗️ | 🛑 |NO❗️ | -| └ | unbindToken | External ❗️ | 🛑 |NO❗️ | -| └ | isTokenBound | Public ❗️ | |NO❗️ | -| └ | _setTokenBinding | Internal 🔒 | 🛑 | | -| └ | _authorizeBoundTokenDocumentManagement | Internal 🔒 | | | -| └ | _checkTokenBound | Internal 🔒 | | | -|||||| -| **VersionModule** | Implementation | IERC8303, ERC165 ||| -| └ | version | Public ❗️ | |NO❗️ | -| └ | supportsInterface | Public ❗️ | |NO❗️ | -|||||| -| **DocumentEngineInvariant** | Implementation | ||| - -### Interfaces - -| Contract | Type | Bases | | | -|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| -| └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | -|||||| -| **IERC1643MultiDocument** | Interface | ||| -| └ | setDocument | External ❗️ | 🛑 |NO❗️ | -| └ | removeDocument | External ❗️ | 🛑 |NO❗️ | -| └ | getDocument | External ❗️ | |NO❗️ | -| └ | getAllDocuments | External ❗️ | |NO❗️ | -|||||| -| **ITokenBinding** | Interface | ||| -| └ | bindToken | External ❗️ | 🛑 |NO❗️ | -| └ | unbindToken | External ❗️ | 🛑 |NO❗️ | -| └ | isTokenBound | External ❗️ | |NO❗️ | -|||||| -| **IERC8303** | Interface | ||| -| └ | version | External ❗️ | |NO❗️ | - - -### Legend - -| Symbol | Meaning | -| :----: | ------------------------- | -| 🛑 | Function can modify state | -| 💵 | Function is payable | - - - -## Gasless support (ERC-2771) - -The DocumentEngine supports client-side gasless transactions using the [Gas Station Network](https://docs.opengsn.org/#the-problem) (GSN) pattern, the main open standard for transfering fee payment to another account than that of the transaction issuer. The contract uses the OpenZeppelin contract `ERC2771ContextUpgradeable`, which allows a contract to get the original client with `_msgSender()` instead of the fee payer given by `msg.sender` while allowing upgrades on the main contract (see *Deployment via a proxy* above). - -At deployment, the parameter `forwarder` inside the constructor has to be set with the defined address of the forwarder. Please note that the forwarder can not be changed after deployment. - -Please see the OpenGSN [documentation](https://docs.opengsn.org/contracts/#receiving-a-relayed-call) for more details on what is done to support GSN in the contract. - - - -## Dependencies - -The toolchain includes the following components, where the versions are the latest ones that we tested: - -- Foundry -- Solidity 0.8.34 (via solc-js), `evm_version = prague` -- OpenZeppelin Contracts (submodule) [v5.7.0](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.7.0) -- Tests - - [CMTAT v3.3.0-rc3](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) - - [RuleEngine v3.0.0-rc5](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) (binding-pattern reference only — its compliance module is [not reused](#why-not-reuse-ruleengines-erc-3643-compliance-module)) - - OpenZeppelin Contracts Upgradeable (submodule) [v5.7.0](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v5.7.0) - -### Version compatibility - -Each release of this engine is built and tested against one CMTAT release. CMTAT's `IERC1643` is -not stable across its own release candidates, so pairing a version of this engine with a different -CMTAT than the one below is not supported. - -| DocumentEngine | CMTAT | Solidity / `evm_version` | OpenZeppelin | `getDocument` returns | -| -------------- | ----- | ------------------------ | ------------ | --------------------- | -| **v0.4.0** (current) | [v3.3.0-rc3](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) | `0.8.34` / `prague` | v5.7.0 | `(string, bytes32, uint256)` | -| v0.3.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | -| v0.2.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | -| v0.1.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | - -Notes on the CMTAT v2 → v3 jump at `v0.4.0`: - -- **CMTAT `v3.3.0-rc1` is not supported.** It is the one release in which `IERC1643.getDocument` - returns a `Document` struct rather than the three flat values; `v3.3.0-rc2` reverted that and - `v3.3.0-rc3` keeps the flat return. rc1 also does not declare `ERC1643InvalidName` / - `ERC1643MissingDocument` on the interface. Building this engine against rc1 fails to compile. -- **`v3.3.0-rc2` → `v3.3.0-rc3` is a no-op for this engine.** The only change to the document - surface (`draft-IERC1643.sol`, `IDocumentEngine.sol`, `DocumentEngineModule.sol`, - `DocumentERC1643Module.sol`) is a pragma bump from `^0.8.20` to `^0.8.24`; the interface, the - errors and the `getDocument` return shape are unchanged. -- The `IERC1643` import path moved in CMTAT v3, from - `CMTAT/interfaces/engine/draft-IERC1643.sol` to `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. -- Document names became `bytes32` in CMTAT v3 (they were `string` up to v2.5.0-rc0). -- Two different Solidity floors apply from `v0.4.0` on, and the sources declare the lower of them: - - **`src/` requires `≥ 0.8.24`** — the pragma every file declares. OpenZeppelin's - `AccessControlEnumerable.sol` / `EnumerableSet.sol` and, since CMTAT `v3.3.0-rc3`, - `draft-IERC1643.sol` are all `^0.8.24`, so no contract here compiles below it. - - **Building the full project, tests included, requires `≥ 0.8.27`**, because CMTAT v3 uses - `require(cond, CustomError())`, which is restricted to the via-ir pipeline before `0.8.27`. - - This is why the declared pragma is `^0.8.24` while `foundry.toml` pins `0.8.34`. - -Exact submodule revisions are pinned in [`foundry.lock`](./foundry.lock). - -## Tools - -### Formatting (forge fmt) - -`forge fmt` is the canonical formatter for this project (configured under `[fmt]` -in `foundry.toml`): +![DocumentEngine and CMTAT call sequence](./doc/img/cmtat-integration-sequence.png) -```bash -forge fmt # format src/, test/, script/ -forge fmt --check # verify formatting (CI) -``` +## Two things integrators must know -### Static analysis +**Read through the subject, not the engine.** The single-argument `getDocument(name)` is +`msg.sender`-scoped, so a third party calling it on the engine reads *its own* — empty — namespace, +with no revert. Read through the token, or use the address-scoped `getDocument(subject, name)`. -Reports are versioned under [`doc/audits/tools/`](./doc/audits/tools), one directory per release, -each with the raw tool output (prefixed by a summary table) and a feedback file triaging every -finding against the source. The security overview is +**The admin path emits nothing on the subject.** A write sent straight to the engine has no +execution point in the token, so only the engine's `DocumentUpdatedForSubject` fires. When consumers +watch the token's address, use the bound-token path. Tracked as `OPEN-2` in [`doc/audits/AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md). -| Release | Tool | Result | Report | Triage | -| ------- | ---- | ------ | ------ | ------ | -| v0.4.0 | Aderyn `0.6.5` | 0 High · 6 Low — **nothing to fix** | [report](./doc/audits/tools/v0.4.0/aderyn/aderyn-report.md) | [feedback](./doc/audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md) | -| v0.4.0 | Slither `0.11.5` | 0 High · 0 Medium · 0 Low · 2 Info — **nothing to fix** | [report](./doc/audits/tools/v0.4.0/slither/slither-report.md) | [feedback](./doc/audits/tools/v0.4.0/slither/slither-report-feedback.md) | -| v0.4.0 | Claude Code (code quality) | 14 findings, **no vulnerability** — 6 implemented, 8 deliberately left | [report](./doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md) | (triage is in the report) | +## Deploy ```bash -# Aderyn — mocks excluded (this project's mocks live in test/, which Aderyn does not scan) -aderyn -x mocks --output doc/audits/tools/v0.4.0/aderyn/aderyn-report.md - -# Slither — mocks excluded (they live in test/, removed by the `test` filter) -slither . --checklist --filter-paths "node_modules,lib,test,forge-std,mocks" \ - > doc/audits/tools/v0.4.0/slither/slither-report.md -``` - -> **Filter on `lib`, not on individual submodule names.** This is a Foundry project, so every -> dependency lives under `lib/`. `--filter-paths` fails *open* — an entry matching nothing silently -> widens scope instead of erroring — so naming submodules one by one risks pulling a whole vendored -> tree into the report. Verify with `grep -c 'lib/\|node_modules/' `, which must return `0`. -> Slither also writes its checklist to **stdout** and its detector log to **stderr**, and exits -> non-zero when it finds anything: `exit=255` with a populated report is the normal outcome. +# role-based +DOCUMENT_ENGINE_ADMIN=0x… DOCUMENT_ENGINE_FORWARDER=0x… \ + forge script script/DeployDocumentEngine.s.sol --rpc-url $RPC_URL --broadcast -> **Static-analysis output is leads, not findings.** Every dismissal in the feedback files was -> verified against the cited `file:line`, and neither tool can see the specification-level issues -> that matter most here — those are tracked under *Known open items* in -> [`AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md). - -### Surya - -Three scripts in [`doc/script`](./doc/script) regenerate the diagrams and reports for every `.sol` -under `src/`, writing into a scratch `docOut/` at the repo root. **Run them from `doc/script/` and in -this order** — the graph script creates `docOut/`, and the report script's `mkdir` has no `-p`: - -```bash -(cd doc/script && bash script_surya_graph.sh) -(cd doc/script && bash script_surya_inheritance.sh) -(cd doc/script && bash script_surya_report.sh) +# owner-based +DOCUMENT_ENGINE_OWNER=0x… DOCUMENT_ENGINE_FORWARDER=0x… \ + forge script script/DeployDocumentEngineOwnable.s.sol --rpc-url $RPC_URL --broadcast ``` -Then replace the three directories under [`doc/surya`](./doc/surya) with the fresh output. Requires -Graphviz (`dot`) — the graph and inheritance scripts pipe through it. - -> **Known Surya bug — check for 0-byte PNGs.** `surya graph` parses only the file it is given, so a -> `super.()` call into a base declared elsewhere throws -> `TypeError: Cannot read properties of undefined (reading 'includes')`. Piped into `dot`, that -> surfaces as a silent **empty PNG**, not an error. Four files here call `super.()` -> (`DocumentEngine`, `DocumentEngineOwnable`, `VersionModule`, `TokenBindingModule`), so the guard in -> `surya/lib/graph.js` — `functionsPerContract[contract] && functionsPerContract[contract].includes(name)` -> — must be applied before regenerating. It lives in `node_modules` (or the `npx` cache) and is -> reverted by any reinstall. - -### Foundry +The forwarder enables ERC-2771 gasless calls and is **immutable**; pass `address(0)` to disable. +Use a keystore or hardware wallet for real deployments, not a raw private key. -Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust. +## More -Foundry consists of: - -- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). -- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. -- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. -- **Chisel**: Fast, utilitarian, and verbose solidity REPL. - -#### Documentation - -https://book.getfoundry.sh/ - -#### Usage - -##### Coverage - -```bash -$ forge coverage --report lcov && genhtml lcov.info --branch-coverage --output-dir coverage -``` - -##### Gas report - -```bash -$ forge test --gas-report -``` +| | | +| --- | --- | +| Full documentation | [`doc/DOCUMENTATION.md`](./doc/DOCUMENTATION.md) | +| Security overview & open items | [`doc/audits/AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md) | +| Static analysis & code-quality reports | [`doc/audits/tools/`](./doc/audits/tools) | +| Release history | [`CHANGELOG.md`](./CHANGELOG.md) | +| Diagrams (Surya, PlantUML) | [`doc/surya/`](./doc/surya), [`doc/img/`](./doc/img) | -##### Build - -```shell -$ forge build -``` - -##### Test - -```shell -$ forge test -``` - -##### Format - -```shell -$ forge fmt -``` - -##### Gas Snapshots - -```shell -$ forge snapshot -``` - -##### Anvil - -```shell -$ anvil -``` - -##### Deploy - -Two deployment scripts are provided in [`script/`](./script), one per access-control -variant. Both read their configuration from environment variables: - -| Variable | Used by | Default | Meaning | -| --- | --- | --- | --- | -| `DOCUMENT_ENGINE_ADMIN` | `DeployDocumentEngine` | `msg.sender` | account granted `DEFAULT_ADMIN_ROLE` | -| `DOCUMENT_ENGINE_OWNER` | `DeployDocumentEngineOwnable` | `msg.sender` | initial owner | -| `DOCUMENT_ENGINE_FORWARDER` | both | `address(0)` | ERC-2771 trusted forwarder (`address(0)` disables gasless) | - -> **Warning** -> -> These environment variables, and passing a raw key with `--private-key`, are -> intended for **local testing only — do not use them in production**. A private -> key supplied on the command line or through an environment variable is exposed -> in your shell history and process environment. For production deployments, use a -> secure signing method (encrypted keystore, hardware wallet, ...) as described in -> the Foundry Key Management documentation (getfoundry.sh) for securely -> broadcasting transactions through a script. - -```shell -# Role-based DocumentEngine (AccessControlEnumerable) -$ DOCUMENT_ENGINE_ADMIN=0xYourAdmin \ - forge script script/DeployDocumentEngine.s.sol \ - --rpc-url --private-key --broadcast - -# Owner-based DocumentEngineOwnable (Ownable2Step) -$ DOCUMENT_ENGINE_OWNER=0xYourOwner \ - forge script script/DeployDocumentEngineOwnable.s.sol \ - --rpc-url --private-key --broadcast -``` - -Drop `--broadcast` (and `--rpc-url`) for a local dry-run. The scripts are covered by -[`test/Deploy.t.sol`](./test/Deploy.t.sol). - -##### Cast - -```shell -$ cast -``` - -##### Help - -```shell -$ forge --help -$ anvil --help -$ cast --help -``` +Current version `0.4.0`, built against CMTAT `v3.3.0-rc3` and OpenZeppelin `v5.7.0` — see the +[version compatibility matrix](./doc/DOCUMENTATION.md#version-compatibility), since this engine is +supported only against the CMTAT release it was built for. ## Intellectual property diff --git a/doc/DOCUMENTATION.md b/doc/DOCUMENTATION.md new file mode 100644 index 0000000..46fec7c --- /dev/null +++ b/doc/DOCUMENTATION.md @@ -0,0 +1,670 @@ +# DocumentEngine — full documentation (ERC-1643) + +> This project has not been audited yet, please use at your own risk. For any questions, please contact [admin@cmta.ch](mailto:admin@cmta.ch). + +This is the complete reference. For a short introduction — what the engine is, how to wire it to a +CMTAT token, and how to deploy it — start at the [root `README.md`](../README.md). + +The `DocumentEngine` is an external contract to manage documents through [*ERC-1643*](https://github.com/ethereum/EIPs/issues/1643), a standard proposition to manage document on-chain. This standard is notably used by [ERC-1400](https://github.com/ethereum/eips/issues/1411) from Polymath. + +The documentEngine is planned to be used by other smart contract,e.g CMTAT token, to store documents on their behalf. + +The ERC-1643 defines a document with three attributes: + +- A short name (represented as a `bytes32`) +- A generic URI (represented as a `string`) that could point to a website or other document portal. +- The hash of the document contents associated with it on-chain. + +A smart contract needs only to read documents from this standard through the interface [IERC1643](../lib/CMTAT/contracts/interfaces/tokenization/draft-IERC1643.sol) to get the documents from the documentEngine: + +```solidity +interface IERC1643 { + error ERC1643InvalidName(); + error ERC1643MissingDocument(); + + function getDocument(bytes32 name) + external + view + returns (string memory uri, bytes32 documentHash, uint256 lastModified); + function getAllDocuments() external view returns (bytes32[] memory documentNames_); + function setDocument(bytes32 name, string calldata uri, bytes32 documentHash) external; + function removeDocument(bytes32 name) external; +} +``` + +> **Note — `getDocument` returns flat values.** CMTAT `v3.3.0-rc1` briefly returned a `Document` +> struct here; `v3.3.0-rc2` restored the three flat return values mandated by the ERC-1643 ABI, and +> this engine follows. The distinction matters because return types are not part of a function +> signature: both shapes have the same selector and the same `type(IERC1643).interfaceId`, so a +> struct return is undetectable through ERC-165 and a consumer built from the specification ABI +> would silently decode it as garbage. The `Document` struct is kept internally for storage only. +> `testGetDocumentReturnsFlatErc1643Abi` pins the wire format. + +Using an external contract for your smart contract provides two advantages: + +- Reduce code size of your smart contract +- Allow to manage documents for several different smart contracts + +### Two ways to manage documents + +The engine supports **two management paths** at the same time: + +**1. Admin path (`DOCUMENT_MANAGER_ROLE`).** Since the engine manages documents +for several different smart contracts, the admin functions take one supplementary +`address smartContract` argument compared to the ERC-1643: + +```solidity +// DocumentEngine (admin overloads) +function setDocument(address smartContract, bytes32 name_, string memory uri_, bytes32 documentHash_) external; +function removeDocument(address smartContract, bytes32 name_) external; +``` + +**2. Bound-token path.** This implements the standard, single-argument ERC-1643 +functions. A token is *bound* to the engine through the shared **`ITokenBinding`** +surface — identical across both deployments, so integrators bind/query a token the +same way regardless of the access-control model: + +```solidity +documentEngine.bindToken(address(token)); // also: unbindToken(token), isTokenBound(token) +``` + +Both deployments share the exact same binding mechanism — a single allowlist in +`TokenBindingModule` (`src/modules/TokenBindingModule.sol`), **not** a role. They +expose the same `bindToken` / `unbindToken` / `isTokenBound` functions, emit the +same `TokenBindingSet` event, and revert with the same `NotBoundToken` error when a +non-bound caller attempts a write. The only difference is *who* may bind: whoever +may manage documents in that deployment (the `DOCUMENT_MANAGER_ROLE` holder, or the +`owner`), since binding is authorized by the same document-management hook. + +Once bound, the token manages its **own** documents (`msg.sender` is the token); +it can never affect another contract's documents: + +```solidity +// DocumentEngine (standard ERC-1643, scoped to msg.sender) +function setDocument(bytes32 name_, string calldata uri_, bytes32 documentHash_) external; +function removeDocument(bytes32 name_) external; +``` + +> This mirrors the RuleEngine *binding* pattern without reusing its +> `ERC3643ComplianceExtendedModule` — see +> [Why not reuse RuleEngine's ERC-3643 compliance module?](#why-not-reuse-ruleengines-erc-3643-compliance-module) below. + +### Flexible access control + +Following the CMTAT / [RuleEngine](https://github.com/CMTA/RuleEngine) pattern, +the restricted functions do not hardcode a check. They carry a **modifier** +(`onlyDocumentManager` / `onlyBoundToken`) that delegates to an **overridable +`internal virtual` authorization hook**: + +- the **admin path** delegates to `_authorizeDocumentManagement()`, the one hook + each deployment implements (`_checkRole(DOCUMENT_MANAGER_ROLE)` for + `DocumentEngine`, `_checkOwner()` for `DocumentEngineOwnable`); +- the **bound-token path** delegates to `_authorizeBoundTokenDocumentManagement()`, + which `TokenBindingModule` implements once for both deployments (it checks the + shared binding allowlist). + +```solidity +// implemented per deployment (the only access-control hook they supply) +function _authorizeDocumentManagement() internal view virtual { + _checkRole(DOCUMENT_MANAGER_ROLE); // or _checkOwner() +} + +// implemented once in TokenBindingModule for both deployments +function _authorizeBoundTokenDocumentManagement() internal view virtual override { + _checkTokenBound(); // reverts NotBoundToken if msg.sender is not bound +} +``` + +This separates the document-management implementation from the authorization +logic: a subclass changes *who* is authorized by overriding the hook, never by +touching the management functions. + +### Why not reuse RuleEngine's ERC-3643 compliance module? + +CMTA's [RuleEngine](https://github.com/CMTA/RuleEngine) (v3) ships an +`ERC3643ComplianceExtendedModule` that offers a ready-made token-binding registry +(`bindToken` / `unbindToken` / `isTokenBound` / `getTokenBounds`). It is tempting +to reuse it for the bound-token path, but we deliberately do **not**, because that +module is an **`IERC3643Compliance`** — a *transfer-compliance* contract. + +Inheriting it would force the DocumentEngine to also implement the ERC-3643 +transfer-compliance callbacks that come with that interface: + +```solidity +function canTransfer(address, address, uint256) external view returns (bool); +function transferred(address, address, uint256) external; +function created(address, uint256) external; +function destroyed(address, uint256) external; +``` + +A document engine has **nothing to do with token transfers**, so these would have +to be stubbed as no-ops (`canTransfer` always returning `true`). That is +misleading: the contract would advertise a transfer-compliance surface it does +not honor, enlarging the ABI and inviting integrators to wire it where a real +compliance contract is expected. + +The binding concept we actually need is tiny — "is this caller a token allowed to +manage its own documents?" — so we implement just that: a **single allowlist** in +`TokenBindingModule`, shared by both deployments and gated by each one's +document-management hook. It is deliberately **not** a role: there is no +`TOKEN_CONTRACT_ROLE`, and `DocumentEngineOwnable` uses the same allowlist rather +than a separate owner-managed one. This keeps the engine's surface honest and +minimal while still mirroring the RuleEngine binding pattern; the RuleEngine +submodule is kept as a reference for that pattern. + +### Events + +This engine is a **shared, multi-token** document manager, so — per the ERC-1643 +["Emission Responsibility"](./ERCSpecification/erc-1643.md) rules — it emits +**only** the address-carrying extension events +`DocumentUpdatedForSubject(address indexed subject, …)` / +`DocumentRemovedForSubject(…)`, and **not** the base `DocumentUpdated` / +`DocumentRemoved` events. The base events carry no address and so cannot identify +which token contract a change belongs to; they are the responsibility of the +token contract that exposes ERC-1643 to consumers (it re-emits them when +delegating). See +the [Multi-Subject Document Management draft](./ERCSpecification/erc-draft_multi_document_management.md) +and the `IERC1643MultiDocument` extension. + +### Integration with CMTAT + +Since CMTAT v3, the shipped standalone tokens store documents on-chain +(`DocumentERC1643Module`) and do not consume an external engine through their +constructor. To use this engine, a CMTAT token relies on the +`DocumentEngineModule` and is wired at runtime with `setDocumentEngine(engine)`; +reads/writes are then forwarded to the engine keyed by the token address. + +#### Architecture + +One engine serves a whole fleet of tokens. Each token keeps its own document +namespace, keyed by its address, and can never reach another token's: + +![DocumentEngine architecture with CMTAT tokens](./img/cmtat-integration-architecture.png) + +_Diagram source: `doc/img/cmtat-integration-architecture.puml`._ + +#### Wiring and call flow + +Two independent steps wire a token to the engine, and they are easy to get half +right: `bindToken(token)` on the **engine** authorises the token to use the +single-argument ERC-1643 functions, while `setDocumentEngine(engine)` on the +**token** tells it where to forward. Bind without wiring and the token has +nowhere to send; wire without binding and the forwarded call reverts +`NotBoundToken`. + +The diagram below also shows the emission split that makes the pair conformant — +and the one case where it does not hold, the admin path: + +![DocumentEngine and CMTAT call sequence](./img/cmtat-integration-sequence.png) + +_Diagram source: `doc/img/cmtat-integration-sequence.puml`._ + +A minimal integration: + +```solidity +// 1. authorise the token on the engine (engine's document manager) +documentEngine.bindToken(address(token)); + +// 2. point the token at the engine (token's document manager) +token.setDocumentEngine(documentEngine); + +// 3. the token now manages its own documents through the standard ERC-1643 calls, +// and reads are forwarded to the engine keyed by the token address +token.setDocument(bytes32("prospectus"), "ipfs://...", keccak256(bytes(content))); +``` + +Both halves are covered by the test suite against real CMTAT code: +`testCanReturnCMTATDocument` wires `CMTATDocumentEngineMock` (built on CMTAT's +`DocumentEngineModule`) with `setDocumentEngine` and reads through it, and +`testBoundTokenCanManageOwnDocument` exercises the bound-token write and the +namespace isolation that goes with it. + + + +## Architecture + +The engine is split into two contracts (CMTAT module/deployment pattern): + +- **`DocumentEngineBase`** (abstract) — holds the document storage and all the + ERC-1643 document-management functions, plus the `onlyDocumentManager` / + `onlyBoundToken` modifiers and the **abstract** `_authorize*` hooks. It is + agnostic to the access-control implementation. +- **`DocumentEngine`** (deployment) — the concrete, deployable contract. It + defines the **access control** (`AccessControlEnumerable`, the `_authorize*` + hook implementations and the `hasRole` override) and wires the ERC-2771 + (gasless) support. `AccessControlEnumerable` additionally allows enumerating + the members of each role on-chain. +- **`DocumentEngineOwnable`** (alternative deployment) — same base logic, but + access control is a single **owner** via `Ownable2Step` (two-step ownership + transfer) instead of roles. Both document management and token binding are + `owner`-only. +- **`TokenBindingModule`** (`src/modules/TokenBindingModule.sol`) — the shared + token-binding registry (an allowlist) implementing `ITokenBinding` + (`bindToken` / `unbindToken` / `isTokenBound` + `TokenBindingSet`). Both + deployments inherit it, so binding is identical (same functions, event, and + `NotBoundToken` revert) and ERC-165-discoverable regardless of the + access-control model; binding is authorized by each deployment's + document-management hook. + +`DocumentEngineInvariant` provides the errors shared by every deployment. +Access-control specifics are **not** defined there: the `DOCUMENT_MANAGER_ROLE` +constant lives in the role-based `DocumentEngine`, and the owner logic in +`DocumentEngineOwnable`. + +`VersionModule` (`src/modules/VersionModule.sol`) isolates the version concern +and implements [ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) +(see below). + +## Version (ERC-8303) + +The contract version is exposed through the `VersionModule`, which implements +the [ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) +`IERC8303` interface: + +```solidity +interface IERC8303 { + function version() external view returns (string memory); +} +``` + +- `version()` returns the current version string (e.g. `"0.4.0"`), following + Semantic Versioning 2.0.0. +- The public `VERSION` constant is kept for backward compatibility and returns + the same value. +- ERC-165 discovery is supported: `supportsInterface(0x54fd4d50)` (the ERC-8303 + interface id) returns `true`. + +### ERC-165: what the engine advertises + +Both deployments advertise: + +| Interface | Id | | +| --- | --- | --- | +| `IERC1643` | `0xecfecec8` | base single-argument functions, for a **bound subject** | +| `IERC1643MultiDocument` | `0xa2b1179b` | address-scoped document management | +| `ITokenBinding` | — | `bindToken` / `unbindToken` / `isTokenBound` | +| `IERC8303` | `0x54fd4d50` | `version()` | +| `IERC165` | `0x01ffc9a7` | | +| `IAccessControlEnumerable` | — | `DocumentEngine` only | + +`type(IERC1643).interfaceId` is advertised because the engine really does implement the base +single-argument functions. Its audience is a **token wiring itself to the engine**: before calling +`setDocumentEngine(engine)`, or before forwarding `setDocument(name, uri, hash)`, a token can confirm +through ERC-165 that those endpoints exist here rather than discovering it from a failed call. +`ITokenBinding` answers the complementary question — does this engine have a binding surface — and +`isTokenBound(address(this))` whether that particular token may use it. + +> **It is not an invitation to read documents from this address.** The base functions are +> `_msgSender()`-scoped, so a third party calling `getDocument(name)` on the engine reads *its own*, +> empty namespace — no revert, no error, just nothing — and the engine emits only the +> address-carrying `*ForSubject` events. Point document consumers at the **subject**, or use the +> address-scoped `getDocument(subject, name)`. Asserted by +> `testBaseERC1643IsAdvertisedButReadsAreCallerScoped`. + +## Schema + +Generated with Surya — regenerate with the three scripts in [`doc/script`](./script). Diagrams +for **every** file in `src/`, interfaces included, live under [`doc/surya`](./surya); the ones +below are the two deployments and the base they share. + +### Inheritance + +Both deployments sit on the same two modules — `DocumentEngineBase` (document logic) and +`TokenBindingModule` (the binding allowlist) — and differ only in the access-control layer. + +#### `DocumentEngine` — role-based (`AccessControlEnumerable`) + +![surya_inheritance_DocumentEngine.sol](./surya/surya_inheritance/surya_inheritance_DocumentEngine.sol.png) + +#### `DocumentEngineOwnable` — single owner (`Ownable2Step`) + +![surya_inheritance_DocumentEngineOwnable.sol](./surya/surya_inheritance/surya_inheritance_DocumentEngineOwnable.sol.png) + +### Graph + +#### `DocumentEngineBase` — the shared document logic + +![surya_graph_DocumentEngineBase.sol](./surya/surya_graph/surya_graph_DocumentEngineBase.sol.png) + +#### `DocumentEngine` + +![surya_graph_DocumentEngine.sol](./surya/surya_graph/surya_graph_DocumentEngine.sol.png) + +#### `DocumentEngineOwnable` + +![surya_graph_DocumentEngineOwnable.sol](./surya/surya_graph/surya_graph_DocumentEngineOwnable.sol.png) + +## Surya Description Report + +### Contracts Description Table + +Per-file reports live in [`doc/surya/surya_report`](./surya/surya_report); the tables below merge +them. Note that the document functions belong to **`DocumentEngineBase`**, not to either deployment — +each deployment contributes only its access-control layer and its ERC-2771 context overrides. + +| Contract | Type | Bases | | | +|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| +| └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | +|||||| +| **DocumentEngineBase** | Implementation | IERC1643, IERC1643MultiDocument, DocumentEngineInvariant, Context ||| +| └ | removeDocument | External ❗️ | 🛑 | onlyDocumentManager | +| └ | setDocument | External ❗️ | 🛑 | onlyBoundToken | +| └ | removeDocument | External ❗️ | 🛑 | onlyBoundToken | +| └ | batchSetDocuments | External ❗️ | 🛑 | onlyDocumentManager | +| └ | batchSetDocuments | External ❗️ | 🛑 | onlyDocumentManager | +| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyDocumentManager | +| └ | batchRemoveDocuments | External ❗️ | 🛑 | onlyDocumentManager | +| └ | getDocument | External ❗️ | |NO❗️ | +| └ | getDocument | External ❗️ | |NO❗️ | +| └ | getAllDocuments | External ❗️ | |NO❗️ | +| └ | getAllDocuments | External ❗️ | |NO❗️ | +| └ | setDocument | Public ❗️ | 🛑 | onlyDocumentManager | +| └ | _removeDocumentName | Internal 🔒 | 🛑 | | +| └ | _removeDocument | Internal 🔒 | 🛑 | | +| └ | _setDocument | Internal 🔒 | 🛑 | | +| └ | _authorizeDocumentManagement | Internal 🔒 | | | +| └ | _authorizeBoundTokenDocumentManagement | Internal 🔒 | | | +| └ | _getDocument | Internal 🔒 | | | +|||||| +| **DocumentEngine** | Implementation | TokenBindingModule, VersionModule, AccessControlEnumerable, ERC2771Context ||| +| └ | | Public ❗️ | 🛑 | ERC2771Context | +| └ | hasRole | Public ❗️ | |NO❗️ | +| └ | supportsInterface | Public ❗️ | |NO❗️ | +| └ | _authorizeDocumentManagement | Internal 🔒 | | | +| └ | _msgSender | Internal 🔒 | | | +| └ | _msgData | Internal 🔒 | | | +| └ | _contextSuffixLength | Internal 🔒 | | | +|||||| +| **DocumentEngineOwnable** | Implementation | TokenBindingModule, VersionModule, Ownable2Step, ERC2771Context ||| +| └ | | Public ❗️ | 🛑 | Ownable ERC2771Context | +| └ | supportsInterface | Public ❗️ | |NO❗️ | +| └ | _authorizeDocumentManagement | Internal 🔒 | | | +| └ | _msgSender | Internal 🔒 | | | +| └ | _msgData | Internal 🔒 | | | +| └ | _contextSuffixLength | Internal 🔒 | | | +|||||| +| **TokenBindingModule** | Implementation | DocumentEngineBase, ITokenBinding ||| +| └ | bindToken | External ❗️ | 🛑 |NO❗️ | +| └ | unbindToken | External ❗️ | 🛑 |NO❗️ | +| └ | isTokenBound | Public ❗️ | |NO❗️ | +| └ | _setTokenBinding | Internal 🔒 | 🛑 | | +| └ | _authorizeBoundTokenDocumentManagement | Internal 🔒 | | | +| └ | _checkTokenBound | Internal 🔒 | | | +|||||| +| **VersionModule** | Implementation | IERC8303, ERC165 ||| +| └ | version | Public ❗️ | |NO❗️ | +| └ | supportsInterface | Public ❗️ | |NO❗️ | +|||||| +| **DocumentEngineInvariant** | Implementation | ||| + +### Interfaces + +| Contract | Type | Bases | | | +|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| +| └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | +|||||| +| **IERC1643MultiDocument** | Interface | ||| +| └ | setDocument | External ❗️ | 🛑 |NO❗️ | +| └ | removeDocument | External ❗️ | 🛑 |NO❗️ | +| └ | getDocument | External ❗️ | |NO❗️ | +| └ | getAllDocuments | External ❗️ | |NO❗️ | +|||||| +| **ITokenBinding** | Interface | ||| +| └ | bindToken | External ❗️ | 🛑 |NO❗️ | +| └ | unbindToken | External ❗️ | 🛑 |NO❗️ | +| └ | isTokenBound | External ❗️ | |NO❗️ | +|||||| +| **IERC8303** | Interface | ||| +| └ | version | External ❗️ | |NO❗️ | + + +### Legend + +| Symbol | Meaning | +| :----: | ------------------------- | +| 🛑 | Function can modify state | +| 💵 | Function is payable | + + + +## Gasless support (ERC-2771) + +The DocumentEngine supports client-side gasless transactions using the [Gas Station Network](https://docs.opengsn.org/#the-problem) (GSN) pattern, the main open standard for transfering fee payment to another account than that of the transaction issuer. The contract uses the OpenZeppelin contract `ERC2771ContextUpgradeable`, which allows a contract to get the original client with `_msgSender()` instead of the fee payer given by `msg.sender` while allowing upgrades on the main contract (see *Deployment via a proxy* above). + +At deployment, the parameter `forwarder` inside the constructor has to be set with the defined address of the forwarder. Please note that the forwarder can not be changed after deployment. + +Please see the OpenGSN [documentation](https://docs.opengsn.org/contracts/#receiving-a-relayed-call) for more details on what is done to support GSN in the contract. + + + +## Dependencies + +The toolchain includes the following components, where the versions are the latest ones that we tested: + +- Foundry +- Solidity 0.8.34 (via solc-js), `evm_version = prague` +- OpenZeppelin Contracts (submodule) [v5.7.0](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.7.0) +- Tests + - [CMTAT v3.3.0-rc3](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) + - [RuleEngine v3.0.0-rc5](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) (binding-pattern reference only — its compliance module is [not reused](#why-not-reuse-ruleengines-erc-3643-compliance-module)) + - OpenZeppelin Contracts Upgradeable (submodule) [v5.7.0](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v5.7.0) + +### Version compatibility + +Each release of this engine is built and tested against one CMTAT release. CMTAT's `IERC1643` is +not stable across its own release candidates, so pairing a version of this engine with a different +CMTAT than the one below is not supported. + +| DocumentEngine | CMTAT | Solidity / `evm_version` | OpenZeppelin | `getDocument` returns | +| -------------- | ----- | ------------------------ | ------------ | --------------------- | +| **v0.4.0** (current) | [v3.3.0-rc3](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) | `0.8.34` / `prague` | v5.7.0 | `(string, bytes32, uint256)` | +| v0.3.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | +| v0.2.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | +| v0.1.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | + +Notes on the CMTAT v2 → v3 jump at `v0.4.0`: + +- **CMTAT `v3.3.0-rc1` is not supported.** It is the one release in which `IERC1643.getDocument` + returns a `Document` struct rather than the three flat values; `v3.3.0-rc2` reverted that and + `v3.3.0-rc3` keeps the flat return. rc1 also does not declare `ERC1643InvalidName` / + `ERC1643MissingDocument` on the interface. Building this engine against rc1 fails to compile. +- **`v3.3.0-rc2` → `v3.3.0-rc3` is a no-op for this engine.** The only change to the document + surface (`draft-IERC1643.sol`, `IDocumentEngine.sol`, `DocumentEngineModule.sol`, + `DocumentERC1643Module.sol`) is a pragma bump from `^0.8.20` to `^0.8.24`; the interface, the + errors and the `getDocument` return shape are unchanged. +- The `IERC1643` import path moved in CMTAT v3, from + `CMTAT/interfaces/engine/draft-IERC1643.sol` to `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. +- Document names became `bytes32` in CMTAT v3 (they were `string` up to v2.5.0-rc0). +- Two different Solidity floors apply from `v0.4.0` on, and the sources declare the lower of them: + - **`src/` requires `≥ 0.8.24`** — the pragma every file declares. OpenZeppelin's + `AccessControlEnumerable.sol` / `EnumerableSet.sol` and, since CMTAT `v3.3.0-rc3`, + `draft-IERC1643.sol` are all `^0.8.24`, so no contract here compiles below it. + - **Building the full project, tests included, requires `≥ 0.8.27`**, because CMTAT v3 uses + `require(cond, CustomError())`, which is restricted to the via-ir pipeline before `0.8.27`. + + This is why the declared pragma is `^0.8.24` while `foundry.toml` pins `0.8.34`. + +Exact submodule revisions are pinned in [`foundry.lock`](../foundry.lock). + +## Tools + +### Formatting (forge fmt) + +`forge fmt` is the canonical formatter for this project (configured under `[fmt]` +in `foundry.toml`): + +```bash +forge fmt # format src/, test/, script/ +forge fmt --check # verify formatting (CI) +``` + +### Static analysis + +Reports are versioned under [`doc/audits/tools/`](./audits/tools), one directory per release, +each with the raw tool output (prefixed by a summary table) and a feedback file triaging every +finding against the source. The security overview is +[`doc/audits/AUDIT_OVERVIEW.md`](./audits/AUDIT_OVERVIEW.md). + +| Release | Tool | Result | Report | Triage | +| ------- | ---- | ------ | ------ | ------ | +| v0.4.0 | Aderyn `0.6.5` | 0 High · 6 Low — **nothing to fix** | [report](./audits/tools/v0.4.0/aderyn/aderyn-report.md) | [feedback](./audits/tools/v0.4.0/aderyn/aderyn-report-feedback.md) | +| v0.4.0 | Slither `0.11.5` | 0 High · 0 Medium · 0 Low · 2 Info — **nothing to fix** | [report](./audits/tools/v0.4.0/slither/slither-report.md) | [feedback](./audits/tools/v0.4.0/slither/slither-report-feedback.md) | +| v0.4.0 | Claude Code (code quality) | 14 findings, **no vulnerability** — 6 implemented, 8 deliberately left | [report](./audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md) | (triage is in the report) | + +```bash +# Aderyn — mocks excluded (this project's mocks live in test/, which Aderyn does not scan) +aderyn -x mocks --output doc/audits/tools/v0.4.0/aderyn/aderyn-report.md + +# Slither — mocks excluded (they live in test/, removed by the `test` filter) +slither . --checklist --filter-paths "node_modules,lib,test,forge-std,mocks" \ + > doc/audits/tools/v0.4.0/slither/slither-report.md +``` + +> **Filter on `lib`, not on individual submodule names.** This is a Foundry project, so every +> dependency lives under `lib/`. `--filter-paths` fails *open* — an entry matching nothing silently +> widens scope instead of erroring — so naming submodules one by one risks pulling a whole vendored +> tree into the report. Verify with `grep -c 'lib/\|node_modules/' `, which must return `0`. +> Slither also writes its checklist to **stdout** and its detector log to **stderr**, and exits +> non-zero when it finds anything: `exit=255` with a populated report is the normal outcome. + +> **Static-analysis output is leads, not findings.** Every dismissal in the feedback files was +> verified against the cited `file:line`, and neither tool can see the specification-level issues +> that matter most here — those are tracked under *Known open items* in +> [`AUDIT_OVERVIEW.md`](./audits/AUDIT_OVERVIEW.md). + +### Surya + +Three scripts in [`doc/script`](./script) regenerate the diagrams and reports for every `.sol` +under `src/`, writing into a scratch `docOut/` at the repo root. **Run them from `doc/script/` and in +this order** — the graph script creates `docOut/`, and the report script's `mkdir` has no `-p`: + +```bash +(cd doc/script && bash script_surya_graph.sh) +(cd doc/script && bash script_surya_inheritance.sh) +(cd doc/script && bash script_surya_report.sh) +``` + +Then replace the three directories under [`doc/surya`](./surya) with the fresh output. Requires +Graphviz (`dot`) — the graph and inheritance scripts pipe through it. + +> **Known Surya bug — check for 0-byte PNGs.** `surya graph` parses only the file it is given, so a +> `super.()` call into a base declared elsewhere throws +> `TypeError: Cannot read properties of undefined (reading 'includes')`. Piped into `dot`, that +> surfaces as a silent **empty PNG**, not an error. Four files here call `super.()` +> (`DocumentEngine`, `DocumentEngineOwnable`, `VersionModule`, `TokenBindingModule`), so the guard in +> `surya/lib/graph.js` — `functionsPerContract[contract] && functionsPerContract[contract].includes(name)` +> — must be applied before regenerating. It lives in `node_modules` (or the `npx` cache) and is +> reverted by any reinstall. + +### Foundry + +Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust. + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose solidity REPL. + +#### Documentation + +https://book.getfoundry.sh/ + +#### Usage + +##### Coverage + +```bash +$ forge coverage --report lcov && genhtml lcov.info --branch-coverage --output-dir coverage +``` + +##### Gas report + +```bash +$ forge test --gas-report +``` + +##### Build + +```shell +$ forge build +``` + +##### Test + +```shell +$ forge test +``` + +##### Format + +```shell +$ forge fmt +``` + +##### Gas Snapshots + +```shell +$ forge snapshot +``` + +##### Anvil + +```shell +$ anvil +``` + +##### Deploy + +Two deployment scripts are provided in [`script/`](../script), one per access-control +variant. Both read their configuration from environment variables: + +| Variable | Used by | Default | Meaning | +| --- | --- | --- | --- | +| `DOCUMENT_ENGINE_ADMIN` | `DeployDocumentEngine` | `msg.sender` | account granted `DEFAULT_ADMIN_ROLE` | +| `DOCUMENT_ENGINE_OWNER` | `DeployDocumentEngineOwnable` | `msg.sender` | initial owner | +| `DOCUMENT_ENGINE_FORWARDER` | both | `address(0)` | ERC-2771 trusted forwarder (`address(0)` disables gasless) | + +> **Warning** +> +> These environment variables, and passing a raw key with `--private-key`, are +> intended for **local testing only — do not use them in production**. A private +> key supplied on the command line or through an environment variable is exposed +> in your shell history and process environment. For production deployments, use a +> secure signing method (encrypted keystore, hardware wallet, ...) as described in +> the Foundry Key Management documentation (getfoundry.sh) for securely +> broadcasting transactions through a script. + +```shell +# Role-based DocumentEngine (AccessControlEnumerable) +$ DOCUMENT_ENGINE_ADMIN=0xYourAdmin \ + forge script script/DeployDocumentEngine.s.sol \ + --rpc-url --private-key --broadcast + +# Owner-based DocumentEngineOwnable (Ownable2Step) +$ DOCUMENT_ENGINE_OWNER=0xYourOwner \ + forge script script/DeployDocumentEngineOwnable.s.sol \ + --rpc-url --private-key --broadcast +``` + +Drop `--broadcast` (and `--rpc-url`) for a local dry-run. The scripts are covered by +[`test/Deploy.t.sol`](../test/Deploy.t.sol). + +##### Cast + +```shell +$ cast +``` + +##### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` + +## Intellectual property + +The code is copyright (c) Capital Market and Technology Association, 2018-2024, and is released under [Mozilla Public License 2.0](https://github.com/CMTA/CMTAT/blob/master/LICENSE.md). From 5698bf2b0895581889f7ff5a8b77cd48f342260c Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:47:21 +0200 Subject: [PATCH 39/47] docs: add SECURITY.md and fix the broken security-policy links in CONTRIBUTING and AUDIT_OVERVIEW --- CONTRIBUTING.md | 2 +- README.md | 1 + SECURITY.md | 5 +++++ doc/audits/AUDIT_OVERVIEW.md | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 SECURITY.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a037f33..0861887 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ There are many ways to contribute to CMTAT Contracts. ## Opening an issue -You can [open an issue] to suggest a feature, a difficulty you have or report a minor bug. For serious bugs in an audited version please do not open an issue, instead refer to our [security policy] for appropriate steps. See [SECURITY.md](./SECURITY.MD). +You can [open an issue](https://github.com/CMTA/DocumentEngine/issues) to suggest a feature, a difficulty you have or report a minor bug. For serious bugs in an audited version please do not open an issue, instead refer to our [security policy](./SECURITY.md) for appropriate steps. Before opening an issue, be sure to search through the existing open and closed issues, and consider posting a comment in one of those instead. diff --git a/README.md b/README.md index 3da310d..cbf78b4 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ Use a keystore or hardware wallet for real deployments, not a raw private key. | Security overview & open items | [`doc/audits/AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md) | | Static analysis & code-quality reports | [`doc/audits/tools/`](./doc/audits/tools) | | Release history | [`CHANGELOG.md`](./CHANGELOG.md) | +| Reporting a vulnerability | [`SECURITY.md`](./SECURITY.md) | | Diagrams (Surya, PlantUML) | [`doc/surya/`](./doc/surya), [`doc/img/`](./doc/img) | Current version `0.4.0`, built against CMTAT `v3.3.0-rc3` and OpenZeppelin `v5.7.0` — see the diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..5a3e608 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,5 @@ +# Security Policy + +## Reporting a Vulnerability + +To report a security vulnerability in this project, please see instruction in [CMTA/CMTAT/SECURITY.md](https://github.com/CMTA/CMTAT/blob/master/SECURITY.md) diff --git a/doc/audits/AUDIT_OVERVIEW.md b/doc/audits/AUDIT_OVERVIEW.md index 8a598f4..f47381c 100644 --- a/doc/audits/AUDIT_OVERVIEW.md +++ b/doc/audits/AUDIT_OVERVIEW.md @@ -82,4 +82,4 @@ they were first given, which is the numbering the audit reports cite. ## Reporting a vulnerability -See the repository's security policy, or contact [admin@cmta.ch](mailto:admin@cmta.ch). +See [`SECURITY.md`](../../SECURITY.md), or contact [admin@cmta.ch](mailto:admin@cmta.ch). From 2f9a44f8af40784f884844d985192097b7ce365f Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:04:43 +0200 Subject: [PATCH 40/47] docs: add simplified write/read diagrams to the README, keep the detailed pair in the documentation --- AGENTS.md | 7 ++++-- CLAUDE.md | 7 ++++-- README.md | 25 +++++++++++++------ doc/img/cmtat-read-simple.png | Bin 0 -> 23556 bytes doc/img/cmtat-read-simple.puml | 43 ++++++++++++++++++++++++++++++++ doc/img/cmtat-write-simple.png | Bin 0 -> 22298 bytes doc/img/cmtat-write-simple.puml | 33 ++++++++++++++++++++++++ 7 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 doc/img/cmtat-read-simple.png create mode 100644 doc/img/cmtat-read-simple.puml create mode 100644 doc/img/cmtat-write-simple.png create mode 100644 doc/img/cmtat-write-simple.puml diff --git a/AGENTS.md b/AGENTS.md index 5da3f24..6054ec1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -164,8 +164,11 @@ Other important files: the full document. - `doc/DOCUMENTATION.md` — the full documentation (Surya schema, ERC-165 rationale, version compatibility matrix, tooling). This is where the old root README moved. -- `doc/img/` — PlantUML **sources** (`*.puml`) plus their rendered `*.png`. The README embeds only - the images. Re-render with `plantuml -tpng doc/img/.puml` after editing a source, and look +- `doc/img/` — PlantUML **sources** (`*.puml`) plus their rendered `*.png`. Two pairs, two + audiences: `cmtat-write-simple` / `cmtat-read-simple` are the **short** ones for `README.md`, + `cmtat-integration-architecture` / `cmtat-integration-sequence` the **detailed** ones for + `doc/DOCUMENTATION.md` — keep the complex pair out of the README. Only images are embedded, never + the source. Re-render with `plantuml -tpng doc/img/.puml` after editing a source, and look at the PNG: PlantUML draws syntax/deprecation warnings *into* the image and still exits 0. - `doc/` — Surya output in `doc/surya/{surya_graph,surya_inheritance,surya_report}`, one file per `.sol` in `src/` (9 each), regenerated by the three scripts in `doc/script/` — run them from that diff --git a/CLAUDE.md b/CLAUDE.md index 5da3f24..6054ec1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -164,8 +164,11 @@ Other important files: the full document. - `doc/DOCUMENTATION.md` — the full documentation (Surya schema, ERC-165 rationale, version compatibility matrix, tooling). This is where the old root README moved. -- `doc/img/` — PlantUML **sources** (`*.puml`) plus their rendered `*.png`. The README embeds only - the images. Re-render with `plantuml -tpng doc/img/.puml` after editing a source, and look +- `doc/img/` — PlantUML **sources** (`*.puml`) plus their rendered `*.png`. Two pairs, two + audiences: `cmtat-write-simple` / `cmtat-read-simple` are the **short** ones for `README.md`, + `cmtat-integration-architecture` / `cmtat-integration-sequence` the **detailed** ones for + `doc/DOCUMENTATION.md` — keep the complex pair out of the README. Only images are embedded, never + the source. Re-render with `plantuml -tpng doc/img/.puml` after editing a source, and look at the PNG: PlantUML draws syntax/deprecation warnings *into* the image and still exits 0. - `doc/` — Surya output in `doc/surya/{surya_graph,surya_inheritance,surya_report}`, one file per `.sol` in `src/` (9 each), regenerated by the three scripts in `doc/script/` — run them from that diff --git a/README.md b/README.md index cbf78b4..4273958 100644 --- a/README.md +++ b/README.md @@ -76,17 +76,28 @@ token.setDocumentEngine(documentEngine); // token's document manager token.setDocument(bytes32("prospectus"), "ipfs://...", keccak256(bytes(content))); ``` -![DocumentEngine and CMTAT call sequence](./doc/img/cmtat-integration-sequence.png) +### Writing a document + +![Writing a document through a CMTAT token](./doc/img/cmtat-write-simple.png) + +### Reading a document + +![Reading a document from a CMTAT token or the engine](./doc/img/cmtat-read-simple.png) + +For the full flow — the wiring steps, every revert branch, and the admin path — see +[the detailed sequence](./doc/DOCUMENTATION.md#integration-with-cmtat) in the documentation. ## Two things integrators must know -**Read through the subject, not the engine.** The single-argument `getDocument(name)` is -`msg.sender`-scoped, so a third party calling it on the engine reads *its own* — empty — namespace, -with no revert. Read through the token, or use the address-scoped `getDocument(subject, name)`. +**Read through the subject, not the engine.** As the read diagram shows, the single-argument +`getDocument(name)` is `msg.sender`-scoped, so a third party calling it on the engine reads *its own* +— empty — namespace, with no revert. Read through the token, or use the address-scoped +`getDocument(subject, name)`. -**The admin path emits nothing on the subject.** A write sent straight to the engine has no -execution point in the token, so only the engine's `DocumentUpdatedForSubject` fires. When consumers -watch the token's address, use the bound-token path. Tracked as `OPEN-2` in +**The admin path emits nothing on the subject.** A write sent straight to the engine +(`setDocument(subject, …)`, rather than through the token as above) has no execution point in the +token, so only the engine's `DocumentUpdatedForSubject` fires. When consumers watch the token's +address, use the bound-token path. Tracked as `OPEN-2` in [`doc/audits/AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md). ## Deploy diff --git a/doc/img/cmtat-read-simple.png b/doc/img/cmtat-read-simple.png new file mode 100644 index 0000000000000000000000000000000000000000..0470f5cfbc5eea5c972ffd0fa5cd8a1cdedc46a7 GIT binary patch literal 23556 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 literal 0 HcmV?d00001 diff --git a/doc/img/cmtat-read-simple.puml b/doc/img/cmtat-read-simple.puml new file mode 100644 index 0000000..4221621 --- /dev/null +++ b/doc/img/cmtat-read-simple.puml @@ -0,0 +1,43 @@ +@startuml +title Reading a document — two valid routes, one trap + +skinparam shadowing false +skinparam sequenceMessageAlign center +skinparam ParticipantPadding 20 + +actor "Consumer" as User +participant "CMTAT token" as Token +participant "DocumentEngine" as Engine + +group Route 1 — ask the token (standard ERC-1643) + User -> Token : getDocument(name) + activate Token + Token -> Engine : getDocument(name) + note right : msg.sender = token + activate Engine + Engine --> Token : uri, hash, lastModified + deactivate Engine + Token --> User : uri, hash, lastModified + deactivate Token +end + +group Route 2 — ask the engine, naming the subject + User -> Engine : getDocument(**token**, name) + activate Engine + Engine --> User : uri, hash, lastModified + deactivate Engine +end + +group #FDEEEE Do not do this + User -> Engine : getDocument(name) + activate Engine + Engine --> User : "", 0x0, 0 + deactivate Engine + note over User, Engine #FDEEEE + The single-argument read is scoped to **msg.sender**, so a + third party reads its **own** empty namespace. It returns + empty values instead of reverting — silent, not an error. + end note +end + +@enduml diff --git a/doc/img/cmtat-write-simple.png b/doc/img/cmtat-write-simple.png new file mode 100644 index 0000000000000000000000000000000000000000..afc8978af2db9292fd6d46d8d6f6a9609b5a1695 GIT binary patch literal 22298 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 Token : setDocument(name, uri, hash) +activate Token + +Token -> Engine : setDocument(name, uri, hash) +note right : msg.sender = token,\nso the token is the subject +activate Engine + +Engine -> Store : store the document +Engine --> Token : emit **DocumentUpdatedForSubject**(token, name, ...) +deactivate Engine + +Token --> Admin : emit **DocumentUpdated**(name, ...) +deactivate Token + +note over Token, Engine #EEF5FF + Two events, on purpose: the engine names the **subject** + (it serves many tokens), the token emits the plain + ERC-1643 event on **its own** address. +end note + +@enduml From 9011ab41a3e07ddc33272d7725622799417ddb46 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:26:50 +0200 Subject: [PATCH 41/47] docs: split the architecture schema into topology + contract structure, reuse the simple diagrams in doc/README.m --- AGENTS.md | 16 ++-- CHANGELOG.md | 4 +- CLAUDE.md | 16 ++-- README.md | 8 +- doc/{DOCUMENTATION.md => README.md} | 42 ++++++---- doc/img/cmtat-integration-architecture.png | Bin 40084 -> 18512 bytes doc/img/cmtat-integration-architecture.puml | 72 +++++------------- doc/img/documentengine-contract-structure.png | Bin 0 -> 29150 bytes .../documentengine-contract-structure.puml | 66 ++++++++++++++++ 9 files changed, 138 insertions(+), 86 deletions(-) rename doc/{DOCUMENTATION.md => README.md} (95%) create mode 100644 doc/img/documentengine-contract-structure.png create mode 100644 doc/img/documentengine-contract-structure.puml diff --git a/AGENTS.md b/AGENTS.md index 6054ec1..61e0a5e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -162,19 +162,21 @@ Other important files: - `README.md` — **short** entry point only: what the engine is, quick start, the two management paths, the CMTAT wiring, the two integrator caveats, deploy. Keep it short; new prose belongs in the full document. -- `doc/DOCUMENTATION.md` — the full documentation (Surya schema, ERC-165 rationale, version +- `doc/README.md` — the full documentation (Surya schema, ERC-165 rationale, version compatibility matrix, tooling). This is where the old root README moved. -- `doc/img/` — PlantUML **sources** (`*.puml`) plus their rendered `*.png`. Two pairs, two - audiences: `cmtat-write-simple` / `cmtat-read-simple` are the **short** ones for `README.md`, - `cmtat-integration-architecture` / `cmtat-integration-sequence` the **detailed** ones for - `doc/DOCUMENTATION.md` — keep the complex pair out of the README. Only images are embedded, never - the source. Re-render with `plantuml -tpng doc/img/.puml` after editing a source, and look +- `doc/img/` — PlantUML **sources** (`*.puml`) plus their rendered `*.png`. Five diagrams, split by + audience: `cmtat-write-simple` / `cmtat-read-simple` are the **short** pair, used in *both* + `README.md` and `doc/README.md`; `cmtat-integration-architecture` (topology), + `documentengine-contract-structure` (inheritance) and `cmtat-integration-sequence` (full call + flow with every revert branch) belong to `doc/README.md` only — keep them out of the root README. + One diagram, one job: when a schema needs a legend to stay legible, split it instead. Only images + are embedded, never the source. Re-render with `plantuml -tpng doc/img/.puml`, and look at the PNG: PlantUML draws syntax/deprecation warnings *into* the image and still exits 0. - `doc/` — Surya output in `doc/surya/{surya_graph,surya_inheritance,surya_report}`, one file per `.sol` in `src/` (9 each), regenerated by the three scripts in `doc/script/` — run them from that directory, **graph first** (it creates the scratch `docOut/`; the report script's `mkdir` lacks `-p`). Patch `surya/lib/graph.js` before regenerating or every contract calling `super.()` - yields a silent 0-byte PNG; see the Surya section in `doc/DOCUMENTATION.md`. Also coverage, and + yields a silent 0-byte PNG; see the Surya section in `doc/README.md`. Also coverage, and `doc/audits/` — the security overview (`AUDIT_OVERVIEW.md`) plus versioned static-analysis output under `doc/audits/tools/vX.Y.Z//`, each with a `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging diff --git a/CHANGELOG.md b/CHANGELOG.md index ae1b63e..2e5abfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,7 +47,7 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) ## v0.4.0 -Targets **CMTAT `v3.3.0-rc3`** — see the [compatibility matrix](./doc/DOCUMENTATION.md#version-compatibility) +Targets **CMTAT `v3.3.0-rc3`** — see the [compatibility matrix](./doc/README.md#version-compatibility) for which CMTAT release each version of this engine is built against. > **Versioning note.** `getDocument` changes shape relative to `v0.3.0`, which the convention above @@ -74,7 +74,7 @@ for which CMTAT release each version of this engine is built against. (8436 bytes; only the CBOR metadata trailer moves, because the source text of `AccessControlEnumerable.sol` changed), and `DocumentEngineOwnable`'s bytecode is unchanged including metadata. - - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) [`v3.0.0-rc5`](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./doc/DOCUMENTATION.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) + - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) [`v3.0.0-rc5`](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./doc/README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) - `foundry.lock` now records every submodule by tag; all five entries had gone stale since `v0.3.0`. - **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) - **Code-quality review** (`doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md`) — 14 findings, none a vulnerability. diff --git a/CLAUDE.md b/CLAUDE.md index 6054ec1..61e0a5e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -162,19 +162,21 @@ Other important files: - `README.md` — **short** entry point only: what the engine is, quick start, the two management paths, the CMTAT wiring, the two integrator caveats, deploy. Keep it short; new prose belongs in the full document. -- `doc/DOCUMENTATION.md` — the full documentation (Surya schema, ERC-165 rationale, version +- `doc/README.md` — the full documentation (Surya schema, ERC-165 rationale, version compatibility matrix, tooling). This is where the old root README moved. -- `doc/img/` — PlantUML **sources** (`*.puml`) plus their rendered `*.png`. Two pairs, two - audiences: `cmtat-write-simple` / `cmtat-read-simple` are the **short** ones for `README.md`, - `cmtat-integration-architecture` / `cmtat-integration-sequence` the **detailed** ones for - `doc/DOCUMENTATION.md` — keep the complex pair out of the README. Only images are embedded, never - the source. Re-render with `plantuml -tpng doc/img/.puml` after editing a source, and look +- `doc/img/` — PlantUML **sources** (`*.puml`) plus their rendered `*.png`. Five diagrams, split by + audience: `cmtat-write-simple` / `cmtat-read-simple` are the **short** pair, used in *both* + `README.md` and `doc/README.md`; `cmtat-integration-architecture` (topology), + `documentengine-contract-structure` (inheritance) and `cmtat-integration-sequence` (full call + flow with every revert branch) belong to `doc/README.md` only — keep them out of the root README. + One diagram, one job: when a schema needs a legend to stay legible, split it instead. Only images + are embedded, never the source. Re-render with `plantuml -tpng doc/img/.puml`, and look at the PNG: PlantUML draws syntax/deprecation warnings *into* the image and still exits 0. - `doc/` — Surya output in `doc/surya/{surya_graph,surya_inheritance,surya_report}`, one file per `.sol` in `src/` (9 each), regenerated by the three scripts in `doc/script/` — run them from that directory, **graph first** (it creates the scratch `docOut/`; the report script's `mkdir` lacks `-p`). Patch `surya/lib/graph.js` before regenerating or every contract calling `super.()` - yields a silent 0-byte PNG; see the Surya section in `doc/DOCUMENTATION.md`. Also coverage, and + yields a silent 0-byte PNG; see the Surya section in `doc/README.md`. Also coverage, and `doc/audits/` — the security overview (`AUDIT_OVERVIEW.md`) plus versioned static-analysis output under `doc/audits/tools/vX.Y.Z//`, each with a `*-report.md` (summary table prepended) and a `*-report-feedback.md` triaging diff --git a/README.md b/README.md index 4273958..880e380 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Why use an external engine rather than storing documents in the token: - lets one operator manage documents for many tokens; - documents can be updated without touching the token. -**Full documentation: [`doc/DOCUMENTATION.md`](./doc/DOCUMENTATION.md).** +**Full documentation: [`doc/README.md`](./doc/README.md).** ## Quick start @@ -85,7 +85,7 @@ token.setDocument(bytes32("prospectus"), "ipfs://...", keccak256(bytes(content)) ![Reading a document from a CMTAT token or the engine](./doc/img/cmtat-read-simple.png) For the full flow — the wiring steps, every revert branch, and the admin path — see -[the detailed sequence](./doc/DOCUMENTATION.md#integration-with-cmtat) in the documentation. +[the detailed sequence](./doc/README.md#integration-with-cmtat) in the documentation. ## Two things integrators must know @@ -119,7 +119,7 @@ Use a keystore or hardware wallet for real deployments, not a raw private key. | | | | --- | --- | -| Full documentation | [`doc/DOCUMENTATION.md`](./doc/DOCUMENTATION.md) | +| Full documentation | [`doc/README.md`](./doc/README.md) | | Security overview & open items | [`doc/audits/AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md) | | Static analysis & code-quality reports | [`doc/audits/tools/`](./doc/audits/tools) | | Release history | [`CHANGELOG.md`](./CHANGELOG.md) | @@ -127,7 +127,7 @@ Use a keystore or hardware wallet for real deployments, not a raw private key. | Diagrams (Surya, PlantUML) | [`doc/surya/`](./doc/surya), [`doc/img/`](./doc/img) | Current version `0.4.0`, built against CMTAT `v3.3.0-rc3` and OpenZeppelin `v5.7.0` — see the -[version compatibility matrix](./doc/DOCUMENTATION.md#version-compatibility), since this engine is +[version compatibility matrix](./doc/README.md#version-compatibility), since this engine is supported only against the CMTAT release it was built for. ## Intellectual property diff --git a/doc/DOCUMENTATION.md b/doc/README.md similarity index 95% rename from doc/DOCUMENTATION.md rename to doc/README.md index 46fec7c..de91310 100644 --- a/doc/DOCUMENTATION.md +++ b/doc/README.md @@ -2,12 +2,9 @@ > This project has not been audited yet, please use at your own risk. For any questions, please contact [admin@cmta.ch](mailto:admin@cmta.ch). -This is the complete reference. For a short introduction — what the engine is, how to wire it to a -CMTAT token, and how to deploy it — start at the [root `README.md`](../README.md). +The `DocumentEngine` is an external contract to manage documents through [*ERC-1643*](https://github.com/ethereum/EIPs/issues/1643), a proposed standard for managing documents on-chain. [ERC-1400](https://github.com/ethereum/eips/issues/1411) from Polymath builds on it. -The `DocumentEngine` is an external contract to manage documents through [*ERC-1643*](https://github.com/ethereum/EIPs/issues/1643), a standard proposition to manage document on-chain. This standard is notably used by [ERC-1400](https://github.com/ethereum/eips/issues/1411) from Polymath. - -The documentEngine is planned to be used by other smart contract,e.g CMTAT token, to store documents on their behalf. +The DocumentEngine is meant to be used by other smart contracts, e.g. a CMTAT token, to store documents on their behalf. The ERC-1643 defines a document with three attributes: @@ -34,10 +31,10 @@ interface IERC1643 { > **Note — `getDocument` returns flat values.** CMTAT `v3.3.0-rc1` briefly returned a `Document` > struct here; `v3.3.0-rc2` restored the three flat return values mandated by the ERC-1643 ABI, and -> this engine follows. The distinction matters because return types are not part of a function -> signature: both shapes have the same selector and the same `type(IERC1643).interfaceId`, so a -> struct return is undetectable through ERC-165 and a consumer built from the specification ABI -> would silently decode it as garbage. The `Document` struct is kept internally for storage only. +> this engine follows. Return types are not part of a function signature, so both shapes share the +> same selector and the same `type(IERC1643).interfaceId`: a struct return is undetectable through +> ERC-165, and a consumer built from the specification ABI decodes it as garbage without reverting. +> The `Document` struct is kept internally for storage only, and > `testGetDocumentReturnsFlatErc1643Abi` pins the wire format. Using an external contract for your smart contract provides two advantages: @@ -174,16 +171,28 @@ constructor. To use this engine, a CMTAT token relies on the `DocumentEngineModule` and is wired at runtime with `setDocumentEngine(engine)`; reads/writes are then forwarded to the engine keyed by the token address. -#### Architecture +#### Topology One engine serves a whole fleet of tokens. Each token keeps its own document namespace, keyed by its address, and can never reach another token's: -![DocumentEngine architecture with CMTAT tokens](./img/cmtat-integration-architecture.png) +![Topology: one engine, many subjects](./img/cmtat-integration-architecture.png) _Diagram source: `doc/img/cmtat-integration-architecture.puml`._ -#### Wiring and call flow +#### Writing a document + +![Writing a document through a CMTAT token](./img/cmtat-write-simple.png) + +_Diagram source: `doc/img/cmtat-write-simple.puml`._ + +#### Reading a document + +![Reading a document from a CMTAT token or the engine](./img/cmtat-read-simple.png) + +_Diagram source: `doc/img/cmtat-read-simple.puml`._ + +#### Wiring and the full call flow Two independent steps wire a token to the engine, and they are easy to get half right: `bindToken(token)` on the **engine** authorises the token to use the @@ -192,8 +201,9 @@ single-argument ERC-1643 functions, while `setDocumentEngine(engine)` on the nowhere to send; wire without binding and the forwarded call reverts `NotBoundToken`. -The diagram below also shows the emission split that makes the pair conformant — -and the one case where it does not hold, the admin path: +The diagram below expands the two above with the wiring steps, every revert +branch, and the admin path — the one case where the emission split does not +hold: ![DocumentEngine and CMTAT call sequence](./img/cmtat-integration-sequence.png) @@ -225,6 +235,10 @@ namespace isolation that goes with it. The engine is split into two contracts (CMTAT module/deployment pattern): +![Contract structure: two deployments over one shared base](./img/documentengine-contract-structure.png) + +_Diagram source: `doc/img/documentengine-contract-structure.puml`._ + - **`DocumentEngineBase`** (abstract) — holds the document storage and all the ERC-1643 document-management functions, plus the `onlyDocumentManager` / `onlyBoundToken` modifiers and the **abstract** `_authorize*` hooks. It is diff --git a/doc/img/cmtat-integration-architecture.png b/doc/img/cmtat-integration-architecture.png index cdc908678a6891727fc39efc778872566ea60f78..3e500e21617340c843d36f2ade935f9c7092c178 100644 GIT binary patch literal 18512 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 BAX5MU literal 40084 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 YE?KU~`>9uRXsAn3Rz;><$~fSE08Jb0WdHyG diff --git a/doc/img/cmtat-integration-architecture.puml b/doc/img/cmtat-integration-architecture.puml index e181b7b..d98d712 100644 --- a/doc/img/cmtat-integration-architecture.puml +++ b/doc/img/cmtat-integration-architecture.puml @@ -1,5 +1,5 @@ @startuml -title DocumentEngine — architecture with CMTAT tokens\nOne engine stores documents for many subjects +title Topology — one engine, many subjects skinparam componentStyle rectangle skinparam shadowing false @@ -14,61 +14,29 @@ skinparam database { BorderColor #666666 } -package "Token side — one contract per issuance" as TokenSide { - component "CMTAT token A\n(subject)" as TokenA - component "CMTAT token B\n(subject)" as TokenB -} - -package "Engine side — one deployment serves the whole fleet" as EngineSide { - component "**DocumentEngine**\nAccessControlEnumerable" as Engine - component "**DocumentEngineOwnable**\nOwnable2Step" as EngineOwnable - - package "shared modules" as Modules { - component "DocumentEngineBase\ndocument logic + storage" as Base - component "TokenBindingModule\nbinding allowlist" as Binding - component "VersionModule\nERC-8303 version()" as Version - } - - database "**_documents**\n[subject][name] -> Document\n**_documentNames**\n[subject] -> bytes32[]" as Store -} - actor "Document manager" as Operator -Engine -[hidden]right- EngineOwnable - -TokenA --> Engine : **bound-token path**\nsubject = msg.sender -TokenB --> Engine - -Operator --> Engine : **admin path**\nsubject is an argument - -Engine --|> Base -Engine --|> Binding -Engine --|> Version -EngineOwnable --|> Base -EngineOwnable --|> Binding -EngineOwnable --|> Version - -Base --> Store : reads / writes\nkeyed by subject - -note bottom of TokenSide - Each token embeds CMTAT's **DocumentEngineModule**: - setDocumentEngine(engine) once, then setDocument / - removeDocument / getDocument are **forwarded** to the - engine. The token re-emits DocumentUpdated and - DocumentRemoved on its **own** address. -end note - -note right of Binding - A token may use the single-argument - ERC-1643 functions only while bound. - Not a role — one allowlist, shared - by both deployments. +component "CMTAT token A" as TokenA +component "CMTAT token B" as TokenB +component "**DocumentEngine**" as Engine +database "documents,\nkeyed by subject" as Store + +TokenA --> Engine : setDocument(name, ...) +TokenB --> Engine : setDocument(name, ...) +Operator --> Engine : setDocument(**subject**, name, ...)\nbindToken(token) +Engine --> Store + +note right of TokenA + **Bound-token path.** The token calls the + single-argument ERC-1643 functions; the + engine uses msg.sender as the subject, so + a token can only touch its own documents. end note -note right of Store - A subject can only ever touch its own - namespace: the bound-token path passes - _msgSender() as the subject. +note right of Operator + **Admin path.** Names the subject + explicitly, so one manager can write + for any contract in the fleet. end note @enduml diff --git a/doc/img/documentengine-contract-structure.png b/doc/img/documentengine-contract-structure.png new file mode 100644 index 0000000000000000000000000000000000000000..3886c8f58611a27cea6ef996df7ca57b49c3505f GIT binary patch literal 29150 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 literal 0 HcmV?d00001 diff --git a/doc/img/documentengine-contract-structure.puml b/doc/img/documentengine-contract-structure.puml new file mode 100644 index 0000000..7b36a95 --- /dev/null +++ b/doc/img/documentengine-contract-structure.puml @@ -0,0 +1,66 @@ +@startuml +title Contract structure — two deployments over one shared base + +skinparam shadowing false +skinparam linetype ortho +skinparam class { + BackgroundColor #FDFDFD + BorderColor #666666 +} +hide empty members +hide circle + +abstract class DocumentEngineBase <> #EEF5FF { + document storage + all ERC-1643 logic + -- + {abstract} _authorizeDocumentManagement() + {abstract} _authorizeBoundTokenDocumentManagement() +} + +abstract class TokenBindingModule <> #EEF5FF { + binding allowlist (ITokenBinding) + -- + implements _authorizeBoundTokenDocumentManagement() +} + +abstract class VersionModule <> #EEF5FF { + ERC-8303 version() +} + +class DocumentEngine #FFF7E6 { + DOCUMENT_MANAGER_ROLE + -- + implements _authorizeDocumentManagement() +} + +class DocumentEngineOwnable #FFF7E6 { + owner + -- + implements _authorizeDocumentManagement() +} + +together { + class AccessControlEnumerable + class Ownable2Step + class ERC2771Context +} + +DocumentEngineBase <|-- TokenBindingModule + +TokenBindingModule <|-- DocumentEngine +VersionModule <|-- DocumentEngine +AccessControlEnumerable <|-- DocumentEngine +ERC2771Context <|-- DocumentEngine + +TokenBindingModule <|-- DocumentEngineOwnable +VersionModule <|-- DocumentEngineOwnable +Ownable2Step <|-- DocumentEngineOwnable +ERC2771Context <|-- DocumentEngineOwnable + +legend bottom + blue = shared by both deployments · orange = the deployment itself · white = OpenZeppelin + A deployment supplies **only** the access-control hook (_authorizeDocumentManagement), + which is the single point where the two differ. +endlegend + +@enduml From 31e19e2b8413ee8c312e12e94626a0cd5b30e64a Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 14:27:22 +0200 Subject: [PATCH 42/47] docs: rewrap CHANGELOG and both READMEs to a consistent 100 columns --- CHANGELOG.md | 275 +++++++++++++++++++++++++++-------------- README.md | 14 ++- doc/README.md | 335 +++++++++++++++++++++++++------------------------- 3 files changed, 357 insertions(+), 267 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e5abfd..0c53d7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,17 @@ # CHANGELOG -Please follow [https://changelog.md](https://changelog.md) conventions and the other conventions below +Please follow [https://changelog.md](https://changelog.md) conventions and the other conventions +below ## Semantic Version 2.0.0 Given a version number MAJOR.MINOR.PATCH, increment the: 1. MAJOR version when the new version makes: - - Incompatible proxy **storage** change internally or through the upgrade of an external library (OpenZeppelin) - - A significant change in external APIs (public/external functions) or in the internal architecture + - Incompatible proxy **storage** change internally or through the upgrade of an external library + (OpenZeppelin) + - A significant change in external APIs (public/external functions) or in the internal + architecture 2. MINOR version when the new version adds functionality in a backward compatible manner 3. PATCH version when the new version makes backward compatible bug fixes @@ -35,97 +38,109 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) > forge fmt - Documentation - - Perform a code coverage and update the files in the corresponding directory [./doc/coverage](./doc/coverage) + - Perform a code coverage and update the files in the corresponding directory + [./doc/coverage](./doc/coverage) (`forge coverage --report lcov --report-file /tmp/lcov-full.info`, then `lcov --extract /tmp/lcov-full.info 'src/*' -o doc/coverage/lcov.info` and - `genhtml doc/coverage/lcov.info --output-directory doc/coverage/coverage`; the `src/*` filter keeps - `test/` and `script/` out of the published figure) - - Perform an audit with several audit tools (Aderyn and Slither), update the report in the corresponding directory [./doc/audits/tools](./doc/audits/tools) + `genhtml doc/coverage/lcov.info --output-directory doc/coverage/coverage`; the `src/*` filter + keeps `test/` and `script/` out of the published figure) + - Perform an audit with several audit tools (Aderyn and Slither), update the report in the + corresponding directory [./doc/audits/tools](./doc/audits/tools) - Update surya doc by running the 3 scripts in [./doc/script](./doc/script) - Update changelog ## v0.4.0 -Targets **CMTAT `v3.3.0-rc3`** — see the [compatibility matrix](./doc/README.md#version-compatibility) -for which CMTAT release each version of this engine is built against. +Targets **CMTAT `v3.3.0-rc3`** — see the +[compatibility matrix](./doc/README.md#version-compatibility) for which CMTAT release each version +of this engine is built against. > **Versioning note.** `getDocument` changes shape relative to `v0.3.0`, which the convention above -> classifies as a MAJOR bump. `MINOR` is used because the project is still in its `0.x` line, where a -> `1.0.0` would wrongly signal a stable, audited release. Treat this release as breaking for any +> classifies as a MAJOR bump. `MINOR` is used because the project is still in its `0.x` line, where +> a `1.0.0` would wrongly signal a stable, audited release. Treat this release as breaking for any > consumer decoding `getDocument`. ### Changed - **Dependencies** - - Upgrade CMTAT `v2.5.0-rc0` → [`v3.3.0-rc3`](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) - (`lib/CMTAT` → `658672f190d56d3f61663a7d6d51962b8980df70`). Development passed through - `v3.3.0-rc1` and `v3.3.0-rc2`. rc1 is **not** compatible with the code as shipped here, because - it declares neither the ERC-1643 errors nor the flat `getDocument` return (see below); rc2 and - rc3 are interchangeable for this engine — between them, the whole document surface + - Upgrade CMTAT `v2.5.0-rc0` → + [`v3.3.0-rc3`](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) (`lib/CMTAT` → + `658672f190d56d3f61663a7d6d51962b8980df70`). Development passed through `v3.3.0-rc1` and + `v3.3.0-rc2`. rc1 is **not** compatible with the code as shipped here, because it declares + neither the ERC-1643 errors nor the flat `getDocument` return (see below); rc2 and rc3 are + interchangeable for this engine — between them, the whole document surface (`draft-IERC1643.sol`, `IDocumentEngine.sol`, `DocumentEngineModule.sol`, `DocumentERC1643Module.sol`) changed only its pragma, `^0.8.20` → `^0.8.24`. - - Upgrade OpenZeppelin Contracts (and Contracts Upgradeable) `v5.0.2` → [`v5.7.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.7.0). - `v5.7.0` deprecates `EnumerableSet.at()` in favour of `pos()` (the old name clashes with a - keyword scheduled for Solidity); `at()` remains as a forwarding alias, and this engine has no - call sites either way. The only exposure is inherited — `AccessControlEnumerable.getRoleMember` - switched to `pos()` internally, with no change to its signature, selector or behaviour. - Verified: `DocumentEngine`'s runtime code is **byte-identical** across `v5.6.1` and `v5.7.0` - (8436 bytes; only the CBOR metadata trailer moves, because the source text of - `AccessControlEnumerable.sol` changed), and `DocumentEngineOwnable`'s bytecode is unchanged - including metadata. - - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) [`v3.0.0-rc5`](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./doc/README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) - - `foundry.lock` now records every submodule by tag; all five entries had gone stale since `v0.3.0`. -- **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) -- **Code-quality review** (`doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md`) — 14 findings, none a vulnerability. - Six implemented: - - **Gas, `_removeDocumentName`**: the `_documentNames[subject]` mapping slot was re-hashed on every - loop iteration; cached as a storage pointer. Measured **−2200 gas** on a 20-entry full scan. + - Upgrade OpenZeppelin Contracts (and Contracts Upgradeable) `v5.0.2` → + [`v5.7.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.7.0). `v5.7.0` + deprecates `EnumerableSet.at()` in favour of `pos()` (the old name clashes with a keyword + scheduled for Solidity); `at()` remains as a forwarding alias, and this engine has no call sites + either way. The only exposure is inherited — `AccessControlEnumerable.getRoleMember` switched to + `pos()` internally, with no change to its signature, selector or behaviour. Verified: + `DocumentEngine`'s runtime code is **byte-identical** across `v5.6.1` and `v5.7.0` (8436 bytes; + only the CBOR metadata trailer moves, because the source text of `AccessControlEnumerable.sol` + changed), and `DocumentEngineOwnable`'s bytecode is unchanged including metadata. + - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) + [`v3.0.0-rc5`](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) as a submodule + (binding-pattern reference; see + [Why not reuse RuleEngine's compliance module?](./doc/README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) + — its `ERC3643ComplianceExtendedModule` is not reused) + - `foundry.lock` now records every submodule by tag; all five entries had gone stale since + `v0.3.0`. +- **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match + CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) +- **Code-quality review** (`doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md`) — 14 findings, none + a vulnerability. Six implemented: + - **Gas, `_removeDocumentName`**: the `_documentNames[subject]` mapping slot was re-hashed on + every loop iteration; cached as a storage pointer. Measured **−2200 gas** on a 20-entry full + scan. - **Gas, `_removeDocument`**: the whole `Document` (URI included) was copied to memory to be read three times; now read through a storage pointer. A further **−645 gas**. Combined, removal is **−2845 gas (−3.3 %)** worst case. The emit must stay ahead of the `delete` — verified by - mutating the order and confirming `testRemoveDocumentEmitsForSubjectEvent` fails. - Side effect: Slither's `incorrect-equality` (Medium) and `timestamp` (Low) stopped firing on the - unchanged `doc.lastModified == 0`, taking it from 4 results to 2. **Not a fix** — both were - already false positives and the detector merely loses the taint through a storage pointer. + mutating the order and confirming `testRemoveDocumentEmitsForSubjectEvent` fails. Side effect: + Slither's `incorrect-equality` (Medium) and `timestamp` (Low) stopped firing on the unchanged + `doc.lastModified == 0`, taking it from 4 results to 2. **Not a fix** — both were already false + positives and the detector merely loses the taint through a storage pointer. - **`hasRole` NatSpec**: documented that a role is **unrevokable from the default admin** — - `revokeRole` succeeds, emits `RoleRevoked` and drops `getRoleMemberCount`, yet the admin keeps the - access. Not a privilege issue (an admin can re-grant itself anything) but the call misreports. - Pinned by the new `testRevokingRoleFromDefaultAdminDoesNotRemoveAccess`. + `revokeRole` succeeds, emits `RoleRevoked` and drops `getRoleMemberCount`, yet the admin keeps + the access. Not a privilege issue (an admin can re-grant itself anything) but the call + misreports. Pinned by the new `testRevokingRoleFromDefaultAdminDoesNotRemoveAccess`. - **`DocumentEngineInvariant`**: the error-location comment misattributed `NotBoundToken(address)` to `ITokenBinding`; it is declared by `TokenBindingModule`. - **Documentation pointers removed from contract comments.** Three comments referenced `doc/ERCSpecification…`; documentation moves but deployed source does not, and this repo had - already renamed that file once (`ERC-1643-proposition.md` → `erc-draft_multi_document_management.md`), - leaving a dangling README link behind. Someone reading verified source on an explorer has the - comment and not the file. All three pointers are gone and each comment is now **shorter**, not - longer — the `IERC1643MultiDocument` header dropped from 10 lines to 9 by replacing an - enumeration that gestured at the draft's rationale with the one operative fact: `subject` need - not be a token. + already renamed that file once (`ERC-1643-proposition.md` → + `erc-draft_multi_document_management.md`), leaving a dangling README link behind. Someone + reading verified source on an explorer has the comment and not the file. All three pointers are + gone and each comment is now **shorter**, not longer — the `IERC1643MultiDocument` header + dropped from 10 lines to 9 by replacing an enumeration that gestured at the draft's rationale + with the one operative fact: `subject` need not be a token. - **All 12 `internal` functions are now `virtual`** (`_setDocument`, `_removeDocument`, `_removeDocumentName`, `_getDocument`, `_setTokenBinding`, `_checkTokenBound`, and the ERC-2771 context trio in both deployments), resolving an inconsistency where `TokenBindingModule` exposed its public surface for override while `DocumentEngineBase` exposed nothing but its two abstract - hooks. A deployment can now override the document write/read paths and the binding check, matching - what CMTAT's equivalent module allows. **Runtime cost is zero:** the executable bytecode of both - deployments is byte-identical before and after (7457 / 6111 bytes, metadata trailer excluded). - Guarded by `OverridingDocumentEngine` + + hooks. A deployment can now override the document write/read paths and the binding check, + matching what CMTAT's equivalent module allows. **Runtime cost is zero:** the executable + bytecode of both deployments is byte-identical before and after (7457 / 6111 bytes, metadata + trailer excluded). Guarded by `OverridingDocumentEngine` + `testInternalHooksAreVirtualAndOverridesAreReached` — removing `virtual` from any of the three overridden hooks fails the build (`Error (4334): Trying to override non-virtual function`). - Notable non-changes, recorded so they are not re-raised: `unchecked { ++i }` buys **0 gas** on solc - 0.8.34 (measured); `string calldata` on the admin `setDocument` is **49 gas worse** than `memory` - (measured); and the duplicated ERC-2771 context overrides **cannot** be extracted into a shared - module — C3 linearization forces each deployment to re-state them, proven by compiler error. -- **Style pass across `src/` and `script/` — behaviour-preserving.** Brought the sources in line with - the Solidity style guide: functions reordered by visibility group (external → public → internal, - `view`/`pure` last within each), so the `_authorize*` hooks and the ERC-2771 context overrides now - follow the public API instead of preceding it; every brace-less global import replaced by a named - one (which required adding the previously implicit `Context` and `AccessControl` imports, since a - named import no longer re-exports a dependency's own imports); and NatSpec completed with a - `@param` per argument and a `@return` per return value. No signature, visibility, body or storage - layout changed — verified by an unchanged per-contract function set, a clean `forge build`, and - 72/72 tests passing. + Notable non-changes, recorded so they are not re-raised: `unchecked { ++i }` buys **0 gas** on + solc 0.8.34 (measured); `string calldata` on the admin `setDocument` is **49 gas worse** than + `memory` (measured); and the duplicated ERC-2771 context overrides **cannot** be extracted into a + shared module — C3 linearization forces each deployment to re-state them, proven by compiler + error. +- **Style pass across `src/` and `script/` — behaviour-preserving.** Brought the sources in line + with the Solidity style guide: functions reordered by visibility group (external → public → + internal, `view`/`pure` last within each), so the `_authorize*` hooks and the ERC-2771 context + overrides now follow the public API instead of preceding it; every brace-less global import + replaced by a named one (which required adding the previously implicit `Context` and + `AccessControl` imports, since a named import no longer re-exports a dependency's own imports); + and NatSpec completed with a `@param` per argument and a `@return` per return value. No signature, + visibility, body or storage layout changed — verified by an unchanged per-contract function set, a + clean `forge build`, and 72/72 tests passing. - **Source pragma raised `^0.8.20` → `^0.8.24`** across `src/`, `script/` and `test/`. This is a correction, not a new restriction: `^0.8.20` had become an over-promise, advertising a range the sources could not actually compile in. OpenZeppelin's `AccessControlEnumerable.sol` and @@ -133,7 +148,8 @@ for which CMTAT release each version of this engine is built against. as well, so every contract in `src/` now transitively requires it — `forge build --use 0.8.23` fails to resolve a compiler. `0.8.24` is the real `src/` floor; the full project including the CMTAT-importing tests needs `0.8.27`, because `require(cond, CustomError())` is restricted to the - via-ir pipeline before then. Deployed bytecode is unaffected — the pinned compiler is still `0.8.34`. + via-ir pipeline before then. Deployed bytecode is unaffected — the pinned compiler is still + `0.8.34`. - **`IERC1643` (CMTAT v3) breaking changes** - `getDocument` keeps returning `(string uri, bytes32 documentHash, uint256 lastModified)` — the flat ERC-1643 ABI — on **both** overloads, `getDocument(bytes32)` and @@ -148,16 +164,19 @@ for which CMTAT release each version of this engine is built against. becomes `0x…60`, and `lastModified` becomes the real hash as a `uint256`. `getDocument` is now covered by `testGetDocumentReturnsFlatErc1643Abi`, which inspects the returndata directly since ERC-165 structurally cannot. - - The `Document` struct and the `DocumentUpdated`/`DocumentRemoved` events are now provided by `IERC1643`; the duplicate local declarations were removed from `DocumentEngineInvariant`. The struct is retained internally for storage only. + - The `Document` struct and the `DocumentUpdated`/`DocumentRemoved` events are now provided by + `IERC1643`; the duplicate local declarations were removed from `DocumentEngineInvariant`. The + struct is retained internally for storage only. - `ERC1643InvalidName()` / `ERC1643MissingDocument()` are likewise declared by `IERC1643` as of CMTAT `v3.3.0-rc2` and are **not** re-declared here. The multi-subject draft requires a contract implementing both interfaces to obtain each error exactly once ("MUST NOT declare them twice"), - and re-declaring is a compile error. Selectors, and hence revert data, are unchanged. - The same principle was applied to every other error: `MultiDocumentInvalidSubject()` moved to + and re-declaring is a compile error. Selectors, and hence revert data, are unchanged. The same + principle was applied to every other error: `MultiDocumentInvalidSubject()` moved to `IERC1643MultiDocument` and `TokenBindingInvalidToken()` is declared on `ITokenBinding`, so an ABI generated from an interface carries its errors. `DocumentEngineInvariant` now holds only `InvalidInputLength` and `AdminWithAddressZeroNotAllowed`, which no interface defines. - - Import path moved: `CMTAT/interfaces/engine/draft-IERC1643.sol` → `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. + - Import path moved: `CMTAT/interfaces/engine/draft-IERC1643.sol` → + `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. - **`ERC1643InvalidSubject()` renamed to `MultiDocumentInvalidSubject()`** and moved from `DocumentEngineInvariant` to `IERC1643MultiDocument`, matching the multi-subject draft. **This @@ -165,59 +184,127 @@ for which CMTAT release each version of this engine is built against. The draft's rule is that an error is prefixed by the proposal that *defines* its condition, not by the one it sits next to. The null-`subject` condition cannot arise in ERC-1643 at all — its - `setDocument` has no `subject` argument, so the subject is implicitly the contract itself, which is - never the null address — so borrowing the `ERC1643` prefix named the error after a standard in - which it is unreachable. The two genuinely-shared errors keep their prefix for the opposite reason. + `setDocument` has no `subject` argument, so the subject is implicitly the contract itself, which + is never the null address — so borrowing the `ERC1643` prefix named the error after a standard in + which it is unreachable. The two genuinely-shared errors keep their prefix for the opposite + reason. - **Token binding rejects `address(0)` and is idempotent.** `bindToken` / `unbindToken` now revert `TokenBindingInvalidToken()` on the null address — which can never call the engine, so binding it granted nothing while still emitting an event indexers key on — and write plus emit - `TokenBindingSet` **only when the binding actually changes**. A repeated call still succeeds, since - the caller's intent already holds, but emits nothing, so every event in the log is a real + `TokenBindingSet` **only when the binding actually changes**. A repeated call still succeeds, + since the caller's intent already holds, but emits nothing, so every event in the log is a real transition and an indexer never has to de-duplicate. ### Added -- **Bound-token document management**: implement the now-mandatory `IERC1643.setDocument(name, uri, hash)` and `removeDocument(name)`, gated by the `onlyBoundToken` modifier and scoped to the caller (`_msgSender()`) own namespace. A token bound with `bindToken(token)` (see the shared binding module below) manages its own documents and can never affect another contract's documents. The admin overloads (explicit `address`, `DOCUMENT_MANAGER_ROLE`) are unchanged, so both systems work side by side. (RuleEngine's `ERC3643ComplianceExtendedModule` was evaluated for the binding but intentionally not reused — see the README.) -- **Optional multi-token events**: alongside the standard `IERC1643` events, the engine now also emits `DocumentUpdatedForContract` / `DocumentRemovedForContract`, which carry the `smartContract` (token) address so off-chain indexers can tell which contract a document belongs to during multi-contract operations. See [`erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md) for the proposed optional standard extension. -- **Flexible access control (CMTAT / RuleEngine pattern)**: the restricted functions use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to overridable `internal virtual` authorization hooks `_authorizeDocumentManagement()` / `_authorizeBoundTokenDocumentManagement()`. Each deployment implements the admin hook (`DOCUMENT_MANAGER_ROLE` or `owner`); the bound-token hook is implemented once by `TokenBindingModule` (the shared allowlist). This separates the document-management implementation from the authorization logic. -- **Split into a base contract and a deployment contract** (CMTAT module/deployment pattern): the document-management logic and storage now live in the new abstract `DocumentEngineBase` (with abstract `_authorize*` hooks), while `DocumentEngine` is the deployment contract that defines the access control (`AccessControl`, the concrete hooks and `hasRole`) and the ERC-2771 wiring. The deployable `DocumentEngine` API and behavior are unchanged. -- **Version module implementing ERC-8303**: the version is now exposed through a dedicated `VersionModule` (`src/modules/VersionModule.sol`) implementing the `IERC8303` interface (`src/interfaces/IERC8303.sol`). It adds a standard `version()` view function (in addition to the existing public `VERSION` constant) and advertises ERC-8303 via ERC-165 (`supportsInterface(0x54fd4d50) == true`). `DocumentEngine` combines the module's `supportsInterface` with the access-control base. -- **Second deployment `DocumentEngineOwnable`** (`src/DocumentEngineOwnable.sol`): an alternative deployment that uses OpenZeppelin `Ownable2Step` (single owner, two-step transfer) instead of role-based access control, reusing the same `DocumentEngineBase` logic and the shared `TokenBindingModule`. Both document management and token binding are restricted to the `owner`. +- **Bound-token document management**: implement the now-mandatory + `IERC1643.setDocument(name, uri, hash)` and `removeDocument(name)`, gated by the `onlyBoundToken` + modifier and scoped to the caller (`_msgSender()`) own namespace. A token bound with + `bindToken(token)` (see the shared binding module below) manages its own documents and can never + affect another contract's documents. The admin overloads (explicit `address`, + `DOCUMENT_MANAGER_ROLE`) are unchanged, so both systems work side by side. (RuleEngine's + `ERC3643ComplianceExtendedModule` was evaluated for the binding but intentionally not reused — see + the README.) +- **Optional multi-token events**: alongside the standard `IERC1643` events, the engine now also + emits `DocumentUpdatedForContract` / `DocumentRemovedForContract`, which carry the `smartContract` + (token) address so off-chain indexers can tell which contract a document belongs to during + multi-contract operations. See + [`erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md) + for the proposed optional standard extension. +- **Flexible access control (CMTAT / RuleEngine pattern)**: the restricted functions use the + `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to overridable + `internal virtual` authorization hooks `_authorizeDocumentManagement()` / + `_authorizeBoundTokenDocumentManagement()`. Each deployment implements the admin hook + (`DOCUMENT_MANAGER_ROLE` or `owner`); the bound-token hook is implemented once by + `TokenBindingModule` (the shared allowlist). This separates the document-management implementation + from the authorization logic. +- **Split into a base contract and a deployment contract** (CMTAT module/deployment pattern): the + document-management logic and storage now live in the new abstract `DocumentEngineBase` (with + abstract `_authorize*` hooks), while `DocumentEngine` is the deployment contract that defines the + access control (`AccessControl`, the concrete hooks and `hasRole`) and the ERC-2771 wiring. The + deployable `DocumentEngine` API and behavior are unchanged. +- **Version module implementing ERC-8303**: the version is now exposed through a dedicated + `VersionModule` (`src/modules/VersionModule.sol`) implementing the `IERC8303` interface + (`src/interfaces/IERC8303.sol`). It adds a standard `version()` view function (in addition to the + existing public `VERSION` constant) and advertises ERC-8303 via ERC-165 + (`supportsInterface(0x54fd4d50) == true`). `DocumentEngine` combines the module's + `supportsInterface` with the access-control base. +- **Second deployment `DocumentEngineOwnable`** (`src/DocumentEngineOwnable.sol`): an alternative + deployment that uses OpenZeppelin `Ownable2Step` (single owner, two-step transfer) instead of + role-based access control, reusing the same `DocumentEngineBase` logic and the shared + `TokenBindingModule`. Both document management and token binding are restricted to the `owner`. ### Changed (access control) -- `DocumentEngine` now inherits **`AccessControlEnumerable`** instead of `AccessControl`, adding on-chain enumeration of role members (`getRoleMember`, `getRoleMemberCount`) and advertising `IAccessControlEnumerable` via ERC-165. Default authorization behavior is unchanged. -- Moved the `DOCUMENT_MANAGER_ROLE` constant out of the shared `DocumentEngineInvariant` and into the role-based `DocumentEngine`, so `DocumentEngineInvariant` (and the `DocumentEngineOwnable` deployment) no longer carry access-control-specific constants. The invariant now holds only the shared errors. +- `DocumentEngine` now inherits **`AccessControlEnumerable`** instead of `AccessControl`, adding + on-chain enumeration of role members (`getRoleMember`, `getRoleMemberCount`) and advertising + `IAccessControlEnumerable` via ERC-165. Default authorization behavior is unchanged. +- Moved the `DOCUMENT_MANAGER_ROLE` constant out of the shared `DocumentEngineInvariant` and into + the role-based `DocumentEngine`, so `DocumentEngineInvariant` (and the `DocumentEngineOwnable` + deployment) no longer carry access-control-specific constants. The invariant now holds only the + shared errors. ### Fixed (ERC-1643 conformance) -Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/erc-1643.md) (which now folds in the multi-token extension and the emission-responsibility rules): - -- **Emission responsibility.** As a shared, multi-token manager the engine now emits **only** the address-carrying extension events and **no longer** emits the base `DocumentUpdated` / `DocumentRemoved` events (the spec's `MUST NOT` for a shared manager — those events carry no `subject` and belong on the token contract). -- **Extension events/interface.** Renamed the multi-token events to the standard `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` (parameter `subject`), and introduced the `IERC1643MultiDocument` interface (`src/interfaces/IERC1643MultiDocument.sol`) that the base now implements — the address-scoped `getDocument` / `getAllDocuments` / `setDocument` / `removeDocument`. -- **Input validation.** `setDocument` now reverts `ERC1643InvalidName()` when `name == bytes32(0)` and `MultiDocumentInvalidSubject()` when `subject == address(0)` (the multi-subject draft's null-namespace guard); `removeDocument` now reverts `ERC1643MissingDocument()` for a non-existent document (previously it silently emitted a spurious removal event). See [`erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md) for the corresponding multi-subject draft. -- **ERC-165 discovery.** `supportsInterface` now returns `true` for `type(IERC1643).interfaceId`, `type(IERC1643MultiDocument).interfaceId` and `type(ITokenBinding).interfaceId` (both deployments). - - The base id is advertised because the engine implements the base single-argument functions, and because a **token** uses it: before wiring itself to the engine with `setDocumentEngine(engine)`, or before forwarding `setDocument(name, uri, hash)`, it can confirm through ERC-165 that those endpoints exist. It does **not** mean documents should be read from the engine's address — the base functions are `_msgSender()`-scoped, so a third-party read returns the caller's own empty namespace. Documented in the README and asserted by `testBaseERC1643IsAdvertisedButReadsAreCallerScoped`. +Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/erc-1643.md) (which +now folds in the multi-token extension and the emission-responsibility rules): + +- **Emission responsibility.** As a shared, multi-token manager the engine now emits **only** the + address-carrying extension events and **no longer** emits the base `DocumentUpdated` / + `DocumentRemoved` events (the spec's `MUST NOT` for a shared manager — those events carry no + `subject` and belong on the token contract). +- **Extension events/interface.** Renamed the multi-token events to the standard + `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` (parameter `subject`), and introduced + the `IERC1643MultiDocument` interface (`src/interfaces/IERC1643MultiDocument.sol`) that the base + now implements — the address-scoped `getDocument` / `getAllDocuments` / `setDocument` / + `removeDocument`. +- **Input validation.** `setDocument` now reverts `ERC1643InvalidName()` when `name == bytes32(0)` + and `MultiDocumentInvalidSubject()` when `subject == address(0)` (the multi-subject draft's + null-namespace guard); `removeDocument` now reverts `ERC1643MissingDocument()` for a non-existent + document (previously it silently emitted a spurious removal event). See + [`erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md) + for the corresponding multi-subject draft. +- **ERC-165 discovery.** `supportsInterface` now returns `true` for `type(IERC1643).interfaceId`, + `type(IERC1643MultiDocument).interfaceId` and `type(ITokenBinding).interfaceId` (both + deployments). + + The base id is advertised because the engine implements the base single-argument functions, and + because a **token** uses it: before wiring itself to the engine with `setDocumentEngine(engine)`, + or before forwarding `setDocument(name, uri, hash)`, it can confirm through ERC-165 that those + endpoints exist. It does **not** mean documents should be read from the engine's address — the + base functions are `_msgSender()`-scoped, so a third-party read returns the caller's own empty + namespace. Documented in the README and asserted by + `testBaseERC1643IsAdvertisedButReadsAreCallerScoped`. ### Added (token binding) -- **Shared `ITokenBinding` interface + `TokenBindingModule`.** `bindToken(token)` / `unbindToken(token)` / `isTokenBound(token)` + `TokenBindingSet` event (`src/interfaces/ITokenBinding.sol`), implemented once for both deployments by `src/modules/TokenBindingModule.sol` — a single **allowlist**, not a role. Both deployments now share the exact same binding mechanism (same functions, event, and `NotBoundToken` revert on an unbound write) and advertise `type(ITokenBinding).interfaceId` via ERC-165. The role deployment **no longer uses `TOKEN_CONTRACT_ROLE`** (removed) — binding is authorized by the document-management hook (`DOCUMENT_MANAGER_ROLE`, or the `owner` in `DocumentEngineOwnable`). +- **Shared `ITokenBinding` interface + `TokenBindingModule`.** `bindToken(token)` / + `unbindToken(token)` / `isTokenBound(token)` + `TokenBindingSet` event + (`src/interfaces/ITokenBinding.sol`), implemented once for both deployments by + `src/modules/TokenBindingModule.sol` — a single **allowlist**, not a role. Both deployments now + share the exact same binding mechanism (same functions, event, and `NotBoundToken` revert on an + unbound write) and advertise `type(ITokenBinding).interfaceId` via ERC-165. The role deployment + **no longer uses `TOKEN_CONTRACT_ROLE`** (removed) — binding is authorized by the + document-management hook (`DOCUMENT_MANAGER_ROLE`, or the `owner` in `DocumentEngineOwnable`). ### Notes / bottlenecks - **Subject-side emission is CMTAT `v3.3.0-rc2` or later.** rc2 made `DocumentEngineModule` re-emit the standard `DocumentUpdated` / `DocumentRemoved` on the **token's own address** after forwarding - to the engine, and revert with `CMTAT_DocumentEngineModule_NoDocumentEngine` when no engine is set. - Combined with this engine emitting only the address-carrying `*ForSubject` events, the + to the engine, and revert with `CMTAT_DocumentEngineModule_NoDocumentEngine` when no engine is + set. Combined with this engine emitting only the address-carrying `*ForSubject` events, the subject-initiated call topology is fully conformant with the multi-subject draft's *Emission Responsibility* rules. The **admin path remains non-conformant by construction** — a write sent - straight to the engine has no execution point in the subject, so the subject emits nothing. - See `OPEN-2` in [`AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md). + straight to the engine has no execution point in the subject, so the subject emits nothing. See + `OPEN-2` in [`AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md). - Open items are tracked under *Known open items* in [`AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md): the most severe is admin-path call topology (`OPEN-2`); also authorization granularity (`OPEN-1`) and enumeration cost (`OPEN-4`). -- CMTAT v3 no longer ships a *standalone* token that consumes an external document engine through its constructor; the standard token stores documents on-chain (`DocumentERC1643Module`). External-engine integration now goes through CMTAT's `DocumentEngineModule` (`setDocumentEngine`). The test suite was updated to exercise this real integration path via a minimal token built on `DocumentEngineModule`. +- CMTAT v3 no longer ships a *standalone* token that consumes an external document engine through + its constructor; the standard token stores documents on-chain (`DocumentERC1643Module`). + External-engine integration now goes through CMTAT's `DocumentEngineModule` (`setDocumentEngine`). + The test suite was updated to exercise this real integration path via a minimal token built on + `DocumentEngineModule`. ## v0.3.0 diff --git a/README.md b/README.md index 880e380..e2802fc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # DocumentEngine (ERC-1643) -> This project has not been audited yet, please use at your own risk. For any questions, please contact [admin@cmta.ch](mailto:admin@cmta.ch). +> This project has not been audited yet, please use at your own risk. For any questions, please +> contact [admin@cmta.ch](mailto:admin@cmta.ch). A standalone contract that stores **[ERC-1643](https://github.com/ethereum/EIPs/issues/1643) documents on-chain on behalf of other contracts** — typically [CMTAT](https://github.com/CMTA/CMTAT) @@ -90,8 +91,8 @@ For the full flow — the wiring steps, every revert branch, and the admin path ## Two things integrators must know **Read through the subject, not the engine.** As the read diagram shows, the single-argument -`getDocument(name)` is `msg.sender`-scoped, so a third party calling it on the engine reads *its own* -— empty — namespace, with no revert. Read through the token, or use the address-scoped +`getDocument(name)` is `msg.sender`-scoped, so a third party calling it on the engine reads *its +own* — empty — namespace, with no revert. Read through the token, or use the address-scoped `getDocument(subject, name)`. **The admin path emits nothing on the subject.** A write sent straight to the engine @@ -112,8 +113,8 @@ DOCUMENT_ENGINE_OWNER=0x… DOCUMENT_ENGINE_FORWARDER=0x… \ forge script script/DeployDocumentEngineOwnable.s.sol --rpc-url $RPC_URL --broadcast ``` -The forwarder enables ERC-2771 gasless calls and is **immutable**; pass `address(0)` to disable. -Use a keystore or hardware wallet for real deployments, not a raw private key. +The forwarder enables ERC-2771 gasless calls and is **immutable**; pass `address(0)` to disable. Use +a keystore or hardware wallet for real deployments, not a raw private key. ## More @@ -132,4 +133,5 @@ supported only against the CMTAT release it was built for. ## Intellectual property -The code is copyright (c) Capital Market and Technology Association, 2018-2024, and is released under [Mozilla Public License 2.0](https://github.com/CMTA/CMTAT/blob/master/LICENSE.md). +The code is copyright (c) Capital Market and Technology Association, 2018-2024, and is released +under [Mozilla Public License 2.0](https://github.com/CMTA/CMTAT/blob/master/LICENSE.md). diff --git a/doc/README.md b/doc/README.md index de91310..df5c16b 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,10 +1,15 @@ # DocumentEngine — full documentation (ERC-1643) -> This project has not been audited yet, please use at your own risk. For any questions, please contact [admin@cmta.ch](mailto:admin@cmta.ch). +> This project has not been audited yet, please use at your own risk. For any questions, please +> contact [admin@cmta.ch](mailto:admin@cmta.ch). -The `DocumentEngine` is an external contract to manage documents through [*ERC-1643*](https://github.com/ethereum/EIPs/issues/1643), a proposed standard for managing documents on-chain. [ERC-1400](https://github.com/ethereum/eips/issues/1411) from Polymath builds on it. +The `DocumentEngine` is an external contract to manage documents through +[*ERC-1643*](https://github.com/ethereum/EIPs/issues/1643), a proposed standard for managing +documents on-chain. [ERC-1400](https://github.com/ethereum/eips/issues/1411) from Polymath builds on +it. -The DocumentEngine is meant to be used by other smart contracts, e.g. a CMTAT token, to store documents on their behalf. +The DocumentEngine is meant to be used by other smart contracts, e.g. a CMTAT token, to store +documents on their behalf. The ERC-1643 defines a document with three attributes: @@ -12,7 +17,9 @@ The ERC-1643 defines a document with three attributes: - A generic URI (represented as a `string`) that could point to a website or other document portal. - The hash of the document contents associated with it on-chain. -A smart contract needs only to read documents from this standard through the interface [IERC1643](../lib/CMTAT/contracts/interfaces/tokenization/draft-IERC1643.sol) to get the documents from the documentEngine: +A smart contract needs only to read documents from this standard through the interface +[IERC1643](../lib/CMTAT/contracts/interfaces/tokenization/draft-IERC1643.sol) to get the documents +from the documentEngine: ```solidity interface IERC1643 { @@ -46,9 +53,9 @@ Using an external contract for your smart contract provides two advantages: The engine supports **two management paths** at the same time: -**1. Admin path (`DOCUMENT_MANAGER_ROLE`).** Since the engine manages documents -for several different smart contracts, the admin functions take one supplementary -`address smartContract` argument compared to the ERC-1643: +**1. Admin path (`DOCUMENT_MANAGER_ROLE`).** Since the engine manages documents for several +different smart contracts, the admin functions take one supplementary `address smartContract` +argument compared to the ERC-1643: ```solidity // DocumentEngine (admin overloads) @@ -56,25 +63,23 @@ function setDocument(address smartContract, bytes32 name_, string memory uri_, b function removeDocument(address smartContract, bytes32 name_) external; ``` -**2. Bound-token path.** This implements the standard, single-argument ERC-1643 -functions. A token is *bound* to the engine through the shared **`ITokenBinding`** -surface — identical across both deployments, so integrators bind/query a token the -same way regardless of the access-control model: +**2. Bound-token path.** This implements the standard, single-argument ERC-1643 functions. A token +is *bound* to the engine through the shared **`ITokenBinding`** surface — identical across both +deployments, so integrators bind/query a token the same way regardless of the access-control model: ```solidity documentEngine.bindToken(address(token)); // also: unbindToken(token), isTokenBound(token) ``` -Both deployments share the exact same binding mechanism — a single allowlist in -`TokenBindingModule` (`src/modules/TokenBindingModule.sol`), **not** a role. They -expose the same `bindToken` / `unbindToken` / `isTokenBound` functions, emit the -same `TokenBindingSet` event, and revert with the same `NotBoundToken` error when a -non-bound caller attempts a write. The only difference is *who* may bind: whoever -may manage documents in that deployment (the `DOCUMENT_MANAGER_ROLE` holder, or the -`owner`), since binding is authorized by the same document-management hook. +Both deployments share the exact same binding mechanism — a single allowlist in `TokenBindingModule` +(`src/modules/TokenBindingModule.sol`), **not** a role. They expose the same `bindToken` / +`unbindToken` / `isTokenBound` functions, emit the same `TokenBindingSet` event, and revert with the +same `NotBoundToken` error when a non-bound caller attempts a write. The only difference is *who* +may bind: whoever may manage documents in that deployment (the `DOCUMENT_MANAGER_ROLE` holder, or +the `owner`), since binding is authorized by the same document-management hook. -Once bound, the token manages its **own** documents (`msg.sender` is the token); -it can never affect another contract's documents: +Once bound, the token manages its **own** documents (`msg.sender` is the token); it can never affect +another contract's documents: ```solidity // DocumentEngine (standard ERC-1643, scoped to msg.sender) @@ -84,21 +89,21 @@ function removeDocument(bytes32 name_) external; > This mirrors the RuleEngine *binding* pattern without reusing its > `ERC3643ComplianceExtendedModule` — see -> [Why not reuse RuleEngine's ERC-3643 compliance module?](#why-not-reuse-ruleengines-erc-3643-compliance-module) below. +> [Why not reuse RuleEngine's ERC-3643 compliance module?](#why-not-reuse-ruleengines-erc-3643-compliance-module) +> below. ### Flexible access control -Following the CMTAT / [RuleEngine](https://github.com/CMTA/RuleEngine) pattern, -the restricted functions do not hardcode a check. They carry a **modifier** -(`onlyDocumentManager` / `onlyBoundToken`) that delegates to an **overridable -`internal virtual` authorization hook**: +Following the CMTAT / [RuleEngine](https://github.com/CMTA/RuleEngine) pattern, the restricted +functions do not hardcode a check. They carry a **modifier** (`onlyDocumentManager` / +`onlyBoundToken`) that delegates to an **overridable `internal virtual` authorization hook**: -- the **admin path** delegates to `_authorizeDocumentManagement()`, the one hook - each deployment implements (`_checkRole(DOCUMENT_MANAGER_ROLE)` for - `DocumentEngine`, `_checkOwner()` for `DocumentEngineOwnable`); -- the **bound-token path** delegates to `_authorizeBoundTokenDocumentManagement()`, - which `TokenBindingModule` implements once for both deployments (it checks the - shared binding allowlist). +- the **admin path** delegates to `_authorizeDocumentManagement()`, the one hook each deployment + implements (`_checkRole(DOCUMENT_MANAGER_ROLE)` for `DocumentEngine`, `_checkOwner()` for + `DocumentEngineOwnable`); +- the **bound-token path** delegates to `_authorizeBoundTokenDocumentManagement()`, which + `TokenBindingModule` implements once for both deployments (it checks the shared binding + allowlist). ```solidity // implemented per deployment (the only access-control hook they supply) @@ -112,20 +117,19 @@ function _authorizeBoundTokenDocumentManagement() internal view virtual override } ``` -This separates the document-management implementation from the authorization -logic: a subclass changes *who* is authorized by overriding the hook, never by -touching the management functions. +This separates the document-management implementation from the authorization logic: a subclass +changes *who* is authorized by overriding the hook, never by touching the management functions. ### Why not reuse RuleEngine's ERC-3643 compliance module? CMTA's [RuleEngine](https://github.com/CMTA/RuleEngine) (v3) ships an -`ERC3643ComplianceExtendedModule` that offers a ready-made token-binding registry -(`bindToken` / `unbindToken` / `isTokenBound` / `getTokenBounds`). It is tempting -to reuse it for the bound-token path, but we deliberately do **not**, because that -module is an **`IERC3643Compliance`** — a *transfer-compliance* contract. +`ERC3643ComplianceExtendedModule` that offers a ready-made token-binding registry (`bindToken` / +`unbindToken` / `isTokenBound` / `getTokenBounds`). It is tempting to reuse it for the bound-token +path, but we deliberately do **not**, because that module is an **`IERC3643Compliance`** — a +*transfer-compliance* contract. -Inheriting it would force the DocumentEngine to also implement the ERC-3643 -transfer-compliance callbacks that come with that interface: +Inheriting it would force the DocumentEngine to also implement the ERC-3643 transfer-compliance +callbacks that come with that interface: ```solidity function canTransfer(address, address, uint256) external view returns (bool); @@ -134,47 +138,42 @@ function created(address, uint256) external; function destroyed(address, uint256) external; ``` -A document engine has **nothing to do with token transfers**, so these would have -to be stubbed as no-ops (`canTransfer` always returning `true`). That is -misleading: the contract would advertise a transfer-compliance surface it does -not honor, enlarging the ABI and inviting integrators to wire it where a real -compliance contract is expected. - -The binding concept we actually need is tiny — "is this caller a token allowed to -manage its own documents?" — so we implement just that: a **single allowlist** in -`TokenBindingModule`, shared by both deployments and gated by each one's -document-management hook. It is deliberately **not** a role: there is no -`TOKEN_CONTRACT_ROLE`, and `DocumentEngineOwnable` uses the same allowlist rather -than a separate owner-managed one. This keeps the engine's surface honest and -minimal while still mirroring the RuleEngine binding pattern; the RuleEngine -submodule is kept as a reference for that pattern. +A document engine has **nothing to do with token transfers**, so these would have to be stubbed as +no-ops (`canTransfer` always returning `true`). That is misleading: the contract would advertise a +transfer-compliance surface it does not honor, enlarging the ABI and inviting integrators to wire it +where a real compliance contract is expected. + +The binding concept we actually need is tiny — "is this caller a token allowed to manage its own +documents?" — so we implement just that: a **single allowlist** in `TokenBindingModule`, shared by +both deployments and gated by each one's document-management hook. It is deliberately **not** a +role: there is no `TOKEN_CONTRACT_ROLE`, and `DocumentEngineOwnable` uses the same allowlist rather +than a separate owner-managed one. This keeps the engine's surface honest and minimal while still +mirroring the RuleEngine binding pattern; the RuleEngine submodule is kept as a reference for that +pattern. ### Events This engine is a **shared, multi-token** document manager, so — per the ERC-1643 -["Emission Responsibility"](./ERCSpecification/erc-1643.md) rules — it emits -**only** the address-carrying extension events -`DocumentUpdatedForSubject(address indexed subject, …)` / -`DocumentRemovedForSubject(…)`, and **not** the base `DocumentUpdated` / -`DocumentRemoved` events. The base events carry no address and so cannot identify -which token contract a change belongs to; they are the responsibility of the -token contract that exposes ERC-1643 to consumers (it re-emits them when -delegating). See -the [Multi-Subject Document Management draft](./ERCSpecification/erc-draft_multi_document_management.md) +["Emission Responsibility"](./ERCSpecification/erc-1643.md) rules — it emits **only** the +address-carrying extension events `DocumentUpdatedForSubject(address indexed subject, …)` / +`DocumentRemovedForSubject(…)`, and **not** the base `DocumentUpdated` / `DocumentRemoved` events. +The base events carry no address and so cannot identify which token contract a change belongs to; +they are the responsibility of the token contract that exposes ERC-1643 to consumers (it re-emits +them when delegating). See the +[Multi-Subject Document Management draft](./ERCSpecification/erc-draft_multi_document_management.md) and the `IERC1643MultiDocument` extension. ### Integration with CMTAT -Since CMTAT v3, the shipped standalone tokens store documents on-chain -(`DocumentERC1643Module`) and do not consume an external engine through their -constructor. To use this engine, a CMTAT token relies on the -`DocumentEngineModule` and is wired at runtime with `setDocumentEngine(engine)`; +Since CMTAT v3, the shipped standalone tokens store documents on-chain (`DocumentERC1643Module`) and +do not consume an external engine through their constructor. To use this engine, a CMTAT token +relies on the `DocumentEngineModule` and is wired at runtime with `setDocumentEngine(engine)`; reads/writes are then forwarded to the engine keyed by the token address. #### Topology -One engine serves a whole fleet of tokens. Each token keeps its own document -namespace, keyed by its address, and can never reach another token's: +One engine serves a whole fleet of tokens. Each token keeps its own document namespace, keyed by its +address, and can never reach another token's: ![Topology: one engine, many subjects](./img/cmtat-integration-architecture.png) @@ -194,16 +193,14 @@ _Diagram source: `doc/img/cmtat-read-simple.puml`._ #### Wiring and the full call flow -Two independent steps wire a token to the engine, and they are easy to get half -right: `bindToken(token)` on the **engine** authorises the token to use the -single-argument ERC-1643 functions, while `setDocumentEngine(engine)` on the -**token** tells it where to forward. Bind without wiring and the token has -nowhere to send; wire without binding and the forwarded call reverts -`NotBoundToken`. +Two independent steps wire a token to the engine, and they are easy to get half right: +`bindToken(token)` on the **engine** authorises the token to use the single-argument ERC-1643 +functions, while `setDocumentEngine(engine)` on the **token** tells it where to forward. Bind +without wiring and the token has nowhere to send; wire without binding and the forwarded call +reverts `NotBoundToken`. -The diagram below expands the two above with the wiring steps, every revert -branch, and the admin path — the one case where the emission split does not -hold: +The diagram below expands the two above with the wiring steps, every revert branch, and the admin +path — the one case where the emission split does not hold: ![DocumentEngine and CMTAT call sequence](./img/cmtat-integration-sequence.png) @@ -223,11 +220,10 @@ token.setDocumentEngine(documentEngine); token.setDocument(bytes32("prospectus"), "ipfs://...", keccak256(bytes(content))); ``` -Both halves are covered by the test suite against real CMTAT code: -`testCanReturnCMTATDocument` wires `CMTATDocumentEngineMock` (built on CMTAT's -`DocumentEngineModule`) with `setDocumentEngine` and reads through it, and -`testBoundTokenCanManageOwnDocument` exercises the bound-token write and the -namespace isolation that goes with it. +Both halves are covered by the test suite against real CMTAT code: `testCanReturnCMTATDocument` +wires `CMTATDocumentEngineMock` (built on CMTAT's `DocumentEngineModule`) with `setDocumentEngine` +and reads through it, and `testBoundTokenCanManageOwnDocument` exercises the bound-token write and +the namespace isolation that goes with it. @@ -239,41 +235,33 @@ The engine is split into two contracts (CMTAT module/deployment pattern): _Diagram source: `doc/img/documentengine-contract-structure.puml`._ -- **`DocumentEngineBase`** (abstract) — holds the document storage and all the - ERC-1643 document-management functions, plus the `onlyDocumentManager` / - `onlyBoundToken` modifiers and the **abstract** `_authorize*` hooks. It is - agnostic to the access-control implementation. -- **`DocumentEngine`** (deployment) — the concrete, deployable contract. It - defines the **access control** (`AccessControlEnumerable`, the `_authorize*` - hook implementations and the `hasRole` override) and wires the ERC-2771 - (gasless) support. `AccessControlEnumerable` additionally allows enumerating - the members of each role on-chain. -- **`DocumentEngineOwnable`** (alternative deployment) — same base logic, but - access control is a single **owner** via `Ownable2Step` (two-step ownership - transfer) instead of roles. Both document management and token binding are - `owner`-only. -- **`TokenBindingModule`** (`src/modules/TokenBindingModule.sol`) — the shared - token-binding registry (an allowlist) implementing `ITokenBinding` - (`bindToken` / `unbindToken` / `isTokenBound` + `TokenBindingSet`). Both - deployments inherit it, so binding is identical (same functions, event, and - `NotBoundToken` revert) and ERC-165-discoverable regardless of the - access-control model; binding is authorized by each deployment's - document-management hook. - -`DocumentEngineInvariant` provides the errors shared by every deployment. -Access-control specifics are **not** defined there: the `DOCUMENT_MANAGER_ROLE` -constant lives in the role-based `DocumentEngine`, and the owner logic in -`DocumentEngineOwnable`. - -`VersionModule` (`src/modules/VersionModule.sol`) isolates the version concern -and implements [ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) -(see below). +- **`DocumentEngineBase`** (abstract) — holds the document storage and all the ERC-1643 + document-management functions, plus the `onlyDocumentManager` / `onlyBoundToken` modifiers and the + **abstract** `_authorize*` hooks. It is agnostic to the access-control implementation. +- **`DocumentEngine`** (deployment) — the concrete, deployable contract. It defines the **access + control** (`AccessControlEnumerable`, the `_authorize*` hook implementations and the `hasRole` + override) and wires the ERC-2771 (gasless) support. `AccessControlEnumerable` additionally allows + enumerating the members of each role on-chain. +- **`DocumentEngineOwnable`** (alternative deployment) — same base logic, but access control is a + single **owner** via `Ownable2Step` (two-step ownership transfer) instead of roles. Both document + management and token binding are `owner`-only. +- **`TokenBindingModule`** (`src/modules/TokenBindingModule.sol`) — the shared token-binding + registry (an allowlist) implementing `ITokenBinding` (`bindToken` / `unbindToken` / `isTokenBound` + + `TokenBindingSet`). Both deployments inherit it, so binding is identical (same functions, event, + and `NotBoundToken` revert) and ERC-165-discoverable regardless of the access-control model; + binding is authorized by each deployment's document-management hook. + +`DocumentEngineInvariant` provides the errors shared by every deployment. Access-control specifics +are **not** defined there: the `DOCUMENT_MANAGER_ROLE` constant lives in the role-based +`DocumentEngine`, and the owner logic in `DocumentEngineOwnable`. + +`VersionModule` (`src/modules/VersionModule.sol`) isolates the version concern and implements +[ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) (see below). ## Version (ERC-8303) -The contract version is exposed through the `VersionModule`, which implements -the [ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) -`IERC8303` interface: +The contract version is exposed through the `VersionModule`, which implements the +[ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) `IERC8303` interface: ```solidity interface IERC8303 { @@ -281,12 +269,11 @@ interface IERC8303 { } ``` -- `version()` returns the current version string (e.g. `"0.4.0"`), following - Semantic Versioning 2.0.0. -- The public `VERSION` constant is kept for backward compatibility and returns - the same value. -- ERC-165 discovery is supported: `supportsInterface(0x54fd4d50)` (the ERC-8303 - interface id) returns `true`. +- `version()` returns the current version string (e.g. `"0.4.0"`), following Semantic Versioning + 2.0.0. +- The public `VERSION` constant is kept for backward compatibility and returns the same value. +- ERC-165 discovery is supported: `supportsInterface(0x54fd4d50)` (the ERC-8303 interface id) + returns `true`. ### ERC-165: what the engine advertises @@ -303,10 +290,10 @@ Both deployments advertise: `type(IERC1643).interfaceId` is advertised because the engine really does implement the base single-argument functions. Its audience is a **token wiring itself to the engine**: before calling -`setDocumentEngine(engine)`, or before forwarding `setDocument(name, uri, hash)`, a token can confirm -through ERC-165 that those endpoints exist here rather than discovering it from a failed call. -`ITokenBinding` answers the complementary question — does this engine have a binding surface — and -`isTokenBound(address(this))` whether that particular token may use it. +`setDocumentEngine(engine)`, or before forwarding `setDocument(name, uri, hash)`, a token can +confirm through ERC-165 that those endpoints exist here rather than discovering it from a failed +call. `ITokenBinding` answers the complementary question — does this engine have a binding surface — +and `isTokenBound(address(this))` whether that particular token may use it. > **It is not an invitation to read documents from this address.** The base functions are > `_msgSender()`-scoped, so a third party calling `getDocument(name)` on the engine reads *its own*, @@ -317,9 +304,9 @@ through ERC-165 that those endpoints exist here rather than discovering it from ## Schema -Generated with Surya — regenerate with the three scripts in [`doc/script`](./script). Diagrams -for **every** file in `src/`, interfaces included, live under [`doc/surya`](./surya); the ones -below are the two deployments and the base they share. +Generated with Surya — regenerate with the three scripts in [`doc/script`](./script). Diagrams for +**every** file in `src/`, interfaces included, live under [`doc/surya`](./surya); the ones below are +the two deployments and the base they share. ### Inheritance @@ -353,8 +340,8 @@ Both deployments sit on the same two modules — `DocumentEngineBase` (document ### Contracts Description Table Per-file reports live in [`doc/surya/surya_report`](./surya/surya_report); the tables below merge -them. Note that the document functions belong to **`DocumentEngineBase`**, not to either deployment — -each deployment contributes only its access-control layer and its ERC-2771 context overrides. +them. Note that the document functions belong to **`DocumentEngineBase`**, not to either deployment +— each deployment contributes only its access-control layer and its ERC-2771 context overrides. | Contract | Type | Bases | | | |:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| @@ -443,31 +430,43 @@ each deployment contributes only its access-control layer and its ERC-2771 conte ## Gasless support (ERC-2771) -The DocumentEngine supports client-side gasless transactions using the [Gas Station Network](https://docs.opengsn.org/#the-problem) (GSN) pattern, the main open standard for transfering fee payment to another account than that of the transaction issuer. The contract uses the OpenZeppelin contract `ERC2771ContextUpgradeable`, which allows a contract to get the original client with `_msgSender()` instead of the fee payer given by `msg.sender` while allowing upgrades on the main contract (see *Deployment via a proxy* above). +The DocumentEngine supports client-side gasless transactions using the +[Gas Station Network](https://docs.opengsn.org/#the-problem) (GSN) pattern, the main open standard +for transfering fee payment to another account than that of the transaction issuer. The contract +uses the OpenZeppelin contract `ERC2771ContextUpgradeable`, which allows a contract to get the +original client with `_msgSender()` instead of the fee payer given by `msg.sender` while allowing +upgrades on the main contract (see *Deployment via a proxy* above). -At deployment, the parameter `forwarder` inside the constructor has to be set with the defined address of the forwarder. Please note that the forwarder can not be changed after deployment. +At deployment, the parameter `forwarder` inside the constructor has to be set with the defined +address of the forwarder. Please note that the forwarder can not be changed after deployment. -Please see the OpenGSN [documentation](https://docs.opengsn.org/contracts/#receiving-a-relayed-call) for more details on what is done to support GSN in the contract. +Please see the OpenGSN [documentation](https://docs.opengsn.org/contracts/#receiving-a-relayed-call) +for more details on what is done to support GSN in the contract. ## Dependencies -The toolchain includes the following components, where the versions are the latest ones that we tested: +The toolchain includes the following components, where the versions are the latest ones that we +tested: - Foundry - Solidity 0.8.34 (via solc-js), `evm_version = prague` -- OpenZeppelin Contracts (submodule) [v5.7.0](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.7.0) +- OpenZeppelin Contracts (submodule) + [v5.7.0](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.7.0) - Tests - [CMTAT v3.3.0-rc3](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) - - [RuleEngine v3.0.0-rc5](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) (binding-pattern reference only — its compliance module is [not reused](#why-not-reuse-ruleengines-erc-3643-compliance-module)) - - OpenZeppelin Contracts Upgradeable (submodule) [v5.7.0](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v5.7.0) + - [RuleEngine v3.0.0-rc5](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) + (binding-pattern reference only — its compliance module is + [not reused](#why-not-reuse-ruleengines-erc-3643-compliance-module)) + - OpenZeppelin Contracts Upgradeable (submodule) + [v5.7.0](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v5.7.0) ### Version compatibility -Each release of this engine is built and tested against one CMTAT release. CMTAT's `IERC1643` is -not stable across its own release candidates, so pairing a version of this engine with a different -CMTAT than the one below is not supported. +Each release of this engine is built and tested against one CMTAT release. CMTAT's `IERC1643` is not +stable across its own release candidates, so pairing a version of this engine with a different CMTAT +than the one below is not supported. | DocumentEngine | CMTAT | Solidity / `evm_version` | OpenZeppelin | `getDocument` returns | | -------------- | ----- | ------------------------ | ------------ | --------------------- | @@ -486,8 +485,8 @@ Notes on the CMTAT v2 → v3 jump at `v0.4.0`: surface (`draft-IERC1643.sol`, `IDocumentEngine.sol`, `DocumentEngineModule.sol`, `DocumentERC1643Module.sol`) is a pragma bump from `^0.8.20` to `^0.8.24`; the interface, the errors and the `getDocument` return shape are unchanged. -- The `IERC1643` import path moved in CMTAT v3, from - `CMTAT/interfaces/engine/draft-IERC1643.sol` to `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. +- The `IERC1643` import path moved in CMTAT v3, from `CMTAT/interfaces/engine/draft-IERC1643.sol` to + `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. - Document names became `bytes32` in CMTAT v3 (they were `string` up to v2.5.0-rc0). - Two different Solidity floors apply from `v0.4.0` on, and the sources declare the lower of them: - **`src/` requires `≥ 0.8.24`** — the pragma every file declares. OpenZeppelin's @@ -504,8 +503,8 @@ Exact submodule revisions are pinned in [`foundry.lock`](../foundry.lock). ### Formatting (forge fmt) -`forge fmt` is the canonical formatter for this project (configured under `[fmt]` -in `foundry.toml`): +`forge fmt` is the canonical formatter for this project (configured under `[fmt]` in +`foundry.toml`): ```bash forge fmt # format src/, test/, script/ @@ -514,9 +513,9 @@ forge fmt --check # verify formatting (CI) ### Static analysis -Reports are versioned under [`doc/audits/tools/`](./audits/tools), one directory per release, -each with the raw tool output (prefixed by a summary table) and a feedback file triaging every -finding against the source. The security overview is +Reports are versioned under [`doc/audits/tools/`](./audits/tools), one directory per release, each +with the raw tool output (prefixed by a summary table) and a feedback file triaging every finding +against the source. The security overview is [`doc/audits/AUDIT_OVERVIEW.md`](./audits/AUDIT_OVERVIEW.md). | Release | Tool | Result | Report | Triage | @@ -548,9 +547,9 @@ slither . --checklist --filter-paths "node_modules,lib,test,forge-std,mocks" \ ### Surya -Three scripts in [`doc/script`](./script) regenerate the diagrams and reports for every `.sol` -under `src/`, writing into a scratch `docOut/` at the repo root. **Run them from `doc/script/` and in -this order** — the graph script creates `docOut/`, and the report script's `mkdir` has no `-p`: +Three scripts in [`doc/script`](./script) regenerate the diagrams and reports for every `.sol` under +`src/`, writing into a scratch `docOut/` at the repo root. **Run them from `doc/script/` and in this +order** — the graph script creates `docOut/`, and the report script's `mkdir` has no `-p`: ```bash (cd doc/script && bash script_surya_graph.sh) @@ -565,19 +564,22 @@ Graphviz (`dot`) — the graph and inheritance scripts pipe through it. > `super.()` call into a base declared elsewhere throws > `TypeError: Cannot read properties of undefined (reading 'includes')`. Piped into `dot`, that > surfaces as a silent **empty PNG**, not an error. Four files here call `super.()` -> (`DocumentEngine`, `DocumentEngineOwnable`, `VersionModule`, `TokenBindingModule`), so the guard in -> `surya/lib/graph.js` — `functionsPerContract[contract] && functionsPerContract[contract].includes(name)` -> — must be applied before regenerating. It lives in `node_modules` (or the `npx` cache) and is -> reverted by any reinstall. +> (`DocumentEngine`, `DocumentEngineOwnable`, `VersionModule`, `TokenBindingModule`), so the guard +> in `surya/lib/graph.js` — +> `functionsPerContract[contract] && functionsPerContract[contract].includes(name)` — must be +> applied before regenerating. It lives in `node_modules` (or the `npx` cache) and is reverted by +> any reinstall. ### Foundry -Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust. +Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written +in Rust. Foundry consists of: - **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). -- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and + getting chain data. - **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. - **Chisel**: Fast, utilitarian, and verbose solidity REPL. @@ -631,8 +633,8 @@ $ anvil ##### Deploy -Two deployment scripts are provided in [`script/`](../script), one per access-control -variant. Both read their configuration from environment variables: +Two deployment scripts are provided in [`script/`](../script), one per access-control variant. Both +read their configuration from environment variables: | Variable | Used by | Default | Meaning | | --- | --- | --- | --- | @@ -641,14 +643,12 @@ variant. Both read their configuration from environment variables: | `DOCUMENT_ENGINE_FORWARDER` | both | `address(0)` | ERC-2771 trusted forwarder (`address(0)` disables gasless) | > **Warning** -> -> These environment variables, and passing a raw key with `--private-key`, are -> intended for **local testing only — do not use them in production**. A private -> key supplied on the command line or through an environment variable is exposed -> in your shell history and process environment. For production deployments, use a -> secure signing method (encrypted keystore, hardware wallet, ...) as described in -> the Foundry Key Management documentation (getfoundry.sh) for securely -> broadcasting transactions through a script. +>These environment variables, and passing a raw key with `--private-key`, are intended for **local +>testing only — do not use them in production**. A private key supplied on the command line or +>through an environment variable is exposed in your shell history and process environment. For +>production deployments, use a secure signing method (encrypted keystore, hardware wallet, ...) as +>described in the Foundry Key Management documentation (getfoundry.sh) for securely broadcasting +>transactions through a script. ```shell # Role-based DocumentEngine (AccessControlEnumerable) @@ -681,4 +681,5 @@ $ cast --help ## Intellectual property -The code is copyright (c) Capital Market and Technology Association, 2018-2024, and is released under [Mozilla Public License 2.0](https://github.com/CMTA/CMTAT/blob/master/LICENSE.md). +The code is copyright (c) Capital Market and Technology Association, 2018-2024, and is released +under [Mozilla Public License 2.0](https://github.com/CMTA/CMTAT/blob/master/LICENSE.md). From 995f7250c1872b01717edb67d5e7472f1dfae40d Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 14:33:52 +0200 Subject: [PATCH 43/47] docs: unwrap prose to one line per block in CHANGELOG and both READMEs, update doc --- AGENTS.md | 2 +- CHANGELOG.md | 300 +++++++++----------------------------------------- CLAUDE.md | 2 +- README.md | 53 +++------ doc/README.md | 291 +++++++++++------------------------------------- 5 files changed, 138 insertions(+), 510 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 61e0a5e..3ae4d61 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -162,7 +162,7 @@ Other important files: - `README.md` — **short** entry point only: what the engine is, quick start, the two management paths, the CMTAT wiring, the two integrator caveats, deploy. Keep it short; new prose belongs in the full document. -- `doc/README.md` — the full documentation (Surya schema, ERC-165 rationale, version +- `doc/README.md` — the specification / full reference (Surya schema, ERC-165 rationale, version compatibility matrix, tooling). This is where the old root README moved. - `doc/img/` — PlantUML **sources** (`*.puml`) plus their rendered `*.png`. Five diagrams, split by audience: `cmtat-write-simple` / `cmtat-read-simple` are the **short** pair, used in *both* diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c53d7c..ccbfc7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,14 @@ # CHANGELOG -Please follow [https://changelog.md](https://changelog.md) conventions and the other conventions -below +Please follow [https://changelog.md](https://changelog.md) conventions and the other conventions below ## Semantic Version 2.0.0 Given a version number MAJOR.MINOR.PATCH, increment the: 1. MAJOR version when the new version makes: - - Incompatible proxy **storage** change internally or through the upgrade of an external library - (OpenZeppelin) - - A significant change in external APIs (public/external functions) or in the internal - architecture + - Incompatible proxy **storage** change internally or through the upgrade of an external library (OpenZeppelin) + - A significant change in external APIs (public/external functions) or in the internal architecture 2. MINOR version when the new version adds functionality in a backward compatible manner 3. PATCH version when the new version makes backward compatible bug fixes @@ -38,273 +35,84 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) > forge fmt - Documentation - - Perform a code coverage and update the files in the corresponding directory - [./doc/coverage](./doc/coverage) - (`forge coverage --report lcov --report-file /tmp/lcov-full.info`, then - `lcov --extract /tmp/lcov-full.info 'src/*' -o doc/coverage/lcov.info` and - `genhtml doc/coverage/lcov.info --output-directory doc/coverage/coverage`; the `src/*` filter - keeps `test/` and `script/` out of the published figure) - - Perform an audit with several audit tools (Aderyn and Slither), update the report in the - corresponding directory [./doc/audits/tools](./doc/audits/tools) + - Perform a code coverage and update the files in the corresponding directory [./doc/coverage](./doc/coverage) (`forge coverage --report lcov --report-file /tmp/lcov-full.info`, then `lcov --extract /tmp/lcov-full.info 'src/*' -o doc/coverage/lcov.info` and `genhtml doc/coverage/lcov.info --output-directory doc/coverage/coverage`; the `src/*` filter keeps `test/` and `script/` out of the published figure) + - Perform an audit with several audit tools (Aderyn and Slither), update the report in the corresponding directory [./doc/audits/tools](./doc/audits/tools) - Update surya doc by running the 3 scripts in [./doc/script](./doc/script) - Update changelog ## v0.4.0 -Targets **CMTAT `v3.3.0-rc3`** — see the -[compatibility matrix](./doc/README.md#version-compatibility) for which CMTAT release each version -of this engine is built against. +Targets **CMTAT `v3.3.0-rc3`** — see the [compatibility matrix](./doc/README.md#version-compatibility) for which CMTAT release each version of this engine is built against. -> **Versioning note.** `getDocument` changes shape relative to `v0.3.0`, which the convention above -> classifies as a MAJOR bump. `MINOR` is used because the project is still in its `0.x` line, where -> a `1.0.0` would wrongly signal a stable, audited release. Treat this release as breaking for any -> consumer decoding `getDocument`. +> **Versioning note.** `getDocument` changes shape relative to `v0.3.0`, which the convention above classifies as a MAJOR bump. `MINOR` is used because the project is still in its `0.x` line, where a `1.0.0` would wrongly signal a stable, audited release. Treat this release as breaking for any consumer decoding `getDocument`. ### Changed - **Dependencies** - - Upgrade CMTAT `v2.5.0-rc0` → - [`v3.3.0-rc3`](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) (`lib/CMTAT` → - `658672f190d56d3f61663a7d6d51962b8980df70`). Development passed through `v3.3.0-rc1` and - `v3.3.0-rc2`. rc1 is **not** compatible with the code as shipped here, because it declares - neither the ERC-1643 errors nor the flat `getDocument` return (see below); rc2 and rc3 are - interchangeable for this engine — between them, the whole document surface - (`draft-IERC1643.sol`, `IDocumentEngine.sol`, `DocumentEngineModule.sol`, - `DocumentERC1643Module.sol`) changed only its pragma, `^0.8.20` → `^0.8.24`. - - Upgrade OpenZeppelin Contracts (and Contracts Upgradeable) `v5.0.2` → - [`v5.7.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.7.0). `v5.7.0` - deprecates `EnumerableSet.at()` in favour of `pos()` (the old name clashes with a keyword - scheduled for Solidity); `at()` remains as a forwarding alias, and this engine has no call sites - either way. The only exposure is inherited — `AccessControlEnumerable.getRoleMember` switched to - `pos()` internally, with no change to its signature, selector or behaviour. Verified: - `DocumentEngine`'s runtime code is **byte-identical** across `v5.6.1` and `v5.7.0` (8436 bytes; - only the CBOR metadata trailer moves, because the source text of `AccessControlEnumerable.sol` - changed), and `DocumentEngineOwnable`'s bytecode is unchanged including metadata. - - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) - [`v3.0.0-rc5`](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) as a submodule - (binding-pattern reference; see - [Why not reuse RuleEngine's compliance module?](./doc/README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) - — its `ERC3643ComplianceExtendedModule` is not reused) - - `foundry.lock` now records every submodule by tag; all five entries had gone stale since - `v0.3.0`. -- **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match - CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) -- **Code-quality review** (`doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md`) — 14 findings, none - a vulnerability. Six implemented: - - **Gas, `_removeDocumentName`**: the `_documentNames[subject]` mapping slot was re-hashed on - every loop iteration; cached as a storage pointer. Measured **−2200 gas** on a 20-entry full - scan. - - **Gas, `_removeDocument`**: the whole `Document` (URI included) was copied to memory to be read - three times; now read through a storage pointer. A further **−645 gas**. Combined, removal is - **−2845 gas (−3.3 %)** worst case. The emit must stay ahead of the `delete` — verified by - mutating the order and confirming `testRemoveDocumentEmitsForSubjectEvent` fails. Side effect: - Slither's `incorrect-equality` (Medium) and `timestamp` (Low) stopped firing on the unchanged - `doc.lastModified == 0`, taking it from 4 results to 2. **Not a fix** — both were already false - positives and the detector merely loses the taint through a storage pointer. - - **`hasRole` NatSpec**: documented that a role is **unrevokable from the default admin** — - `revokeRole` succeeds, emits `RoleRevoked` and drops `getRoleMemberCount`, yet the admin keeps - the access. Not a privilege issue (an admin can re-grant itself anything) but the call - misreports. Pinned by the new `testRevokingRoleFromDefaultAdminDoesNotRemoveAccess`. - - **`DocumentEngineInvariant`**: the error-location comment misattributed `NotBoundToken(address)` - to `ITokenBinding`; it is declared by `TokenBindingModule`. - - **Documentation pointers removed from contract comments.** Three comments referenced - `doc/ERCSpecification…`; documentation moves but deployed source does not, and this repo had - already renamed that file once (`ERC-1643-proposition.md` → - `erc-draft_multi_document_management.md`), leaving a dangling README link behind. Someone - reading verified source on an explorer has the comment and not the file. All three pointers are - gone and each comment is now **shorter**, not longer — the `IERC1643MultiDocument` header - dropped from 10 lines to 9 by replacing an enumeration that gestured at the draft's rationale - with the one operative fact: `subject` need not be a token. - - **All 12 `internal` functions are now `virtual`** (`_setDocument`, `_removeDocument`, - `_removeDocumentName`, `_getDocument`, `_setTokenBinding`, `_checkTokenBound`, and the ERC-2771 - context trio in both deployments), resolving an inconsistency where `TokenBindingModule` exposed - its public surface for override while `DocumentEngineBase` exposed nothing but its two abstract - hooks. A deployment can now override the document write/read paths and the binding check, - matching what CMTAT's equivalent module allows. **Runtime cost is zero:** the executable - bytecode of both deployments is byte-identical before and after (7457 / 6111 bytes, metadata - trailer excluded). Guarded by `OverridingDocumentEngine` + - `testInternalHooksAreVirtualAndOverridesAreReached` — removing `virtual` from any of the three - overridden hooks fails the build (`Error (4334): Trying to override non-virtual function`). - - Notable non-changes, recorded so they are not re-raised: `unchecked { ++i }` buys **0 gas** on - solc 0.8.34 (measured); `string calldata` on the admin `setDocument` is **49 gas worse** than - `memory` (measured); and the duplicated ERC-2771 context overrides **cannot** be extracted into a - shared module — C3 linearization forces each deployment to re-state them, proven by compiler - error. -- **Style pass across `src/` and `script/` — behaviour-preserving.** Brought the sources in line - with the Solidity style guide: functions reordered by visibility group (external → public → - internal, `view`/`pure` last within each), so the `_authorize*` hooks and the ERC-2771 context - overrides now follow the public API instead of preceding it; every brace-less global import - replaced by a named one (which required adding the previously implicit `Context` and - `AccessControl` imports, since a named import no longer re-exports a dependency's own imports); - and NatSpec completed with a `@param` per argument and a `@return` per return value. No signature, - visibility, body or storage layout changed — verified by an unchanged per-contract function set, a - clean `forge build`, and 72/72 tests passing. -- **Source pragma raised `^0.8.20` → `^0.8.24`** across `src/`, `script/` and `test/`. This is a - correction, not a new restriction: `^0.8.20` had become an over-promise, advertising a range the - sources could not actually compile in. OpenZeppelin's `AccessControlEnumerable.sol` and - `EnumerableSet.sol` are `^0.8.24`, and CMTAT `v3.3.0-rc3` moved `draft-IERC1643.sol` to `^0.8.24` - as well, so every contract in `src/` now transitively requires it — `forge build --use 0.8.23` - fails to resolve a compiler. `0.8.24` is the real `src/` floor; the full project including the - CMTAT-importing tests needs `0.8.27`, because `require(cond, CustomError())` is restricted to the - via-ir pipeline before then. Deployed bytecode is unaffected — the pinned compiler is still - `0.8.34`. + - Upgrade CMTAT `v2.5.0-rc0` → [`v3.3.0-rc3`](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) (`lib/CMTAT` → `658672f190d56d3f61663a7d6d51962b8980df70`). Development passed through `v3.3.0-rc1` and `v3.3.0-rc2`. rc1 is **not** compatible with the code as shipped here, because it declares neither the ERC-1643 errors nor the flat `getDocument` return (see below); rc2 and rc3 are interchangeable for this engine — between them, the whole document surface (`draft-IERC1643.sol`, `IDocumentEngine.sol`, `DocumentEngineModule.sol`, `DocumentERC1643Module.sol`) changed only its pragma, `^0.8.20` → `^0.8.24`. + - Upgrade OpenZeppelin Contracts (and Contracts Upgradeable) `v5.0.2` → [`v5.7.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.7.0). `v5.7.0` deprecates `EnumerableSet.at()` in favour of `pos()` (the old name clashes with a keyword scheduled for Solidity); `at()` remains as a forwarding alias, and this engine has no call sites either way. The only exposure is inherited — `AccessControlEnumerable.getRoleMember` switched to `pos()` internally, with no change to its signature, selector or behaviour. Verified: `DocumentEngine`'s runtime code is **byte-identical** across `v5.6.1` and `v5.7.0` (8436 bytes; only the CBOR metadata trailer moves, because the source text of `AccessControlEnumerable.sol` changed), and `DocumentEngineOwnable`'s bytecode is unchanged including metadata. + - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) [`v3.0.0-rc5`](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./doc/README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) + - `foundry.lock` now records every submodule by tag; all five entries had gone stale since `v0.3.0`. +- **Toolchain**: bump Solidity `0.8.26` → `0.8.34` and `evm_version` `cancun` → `prague` to match CMTAT v3 (CMTAT uses `require(cond, CustomError())`, which needs solc ≥ 0.8.27) +- **Code-quality review** (`doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md`) — 14 findings, none a vulnerability. Six implemented: + - **Gas, `_removeDocumentName`**: the `_documentNames[subject]` mapping slot was re-hashed on every loop iteration; cached as a storage pointer. Measured **−2200 gas** on a 20-entry full scan. + - **Gas, `_removeDocument`**: the whole `Document` (URI included) was copied to memory to be read three times; now read through a storage pointer. A further **−645 gas**. Combined, removal is **−2845 gas (−3.3 %)** worst case. The emit must stay ahead of the `delete` — verified by mutating the order and confirming `testRemoveDocumentEmitsForSubjectEvent` fails. Side effect: Slither's `incorrect-equality` (Medium) and `timestamp` (Low) stopped firing on the unchanged `doc.lastModified == 0`, taking it from 4 results to 2. **Not a fix** — both were already false positives and the detector merely loses the taint through a storage pointer. + - **`hasRole` NatSpec**: documented that a role is **unrevokable from the default admin** — `revokeRole` succeeds, emits `RoleRevoked` and drops `getRoleMemberCount`, yet the admin keeps the access. Not a privilege issue (an admin can re-grant itself anything) but the call misreports. Pinned by the new `testRevokingRoleFromDefaultAdminDoesNotRemoveAccess`. + - **`DocumentEngineInvariant`**: the error-location comment misattributed `NotBoundToken(address)` to `ITokenBinding`; it is declared by `TokenBindingModule`. + - **Documentation pointers removed from contract comments.** Three comments referenced `doc/ERCSpecification…`; documentation moves but deployed source does not, and this repo had already renamed that file once (`ERC-1643-proposition.md` → `erc-draft_multi_document_management.md`), leaving a dangling README link behind. Someone reading verified source on an explorer has the comment and not the file. All three pointers are gone and each comment is now **shorter**, not longer — the `IERC1643MultiDocument` header dropped from 10 lines to 9 by replacing an enumeration that gestured at the draft's rationale with the one operative fact: `subject` need not be a token. + - **All 12 `internal` functions are now `virtual`** (`_setDocument`, `_removeDocument`, `_removeDocumentName`, `_getDocument`, `_setTokenBinding`, `_checkTokenBound`, and the ERC-2771 context trio in both deployments), resolving an inconsistency where `TokenBindingModule` exposed its public surface for override while `DocumentEngineBase` exposed nothing but its two abstract hooks. A deployment can now override the document write/read paths and the binding check, matching what CMTAT's equivalent module allows. **Runtime cost is zero:** the executable bytecode of both deployments is byte-identical before and after (7457 / 6111 bytes, metadata trailer excluded). Guarded by `OverridingDocumentEngine` + `testInternalHooksAreVirtualAndOverridesAreReached` — removing `virtual` from any of the three overridden hooks fails the build (`Error (4334): Trying to override non-virtual function`). + + Notable non-changes, recorded so they are not re-raised: `unchecked { ++i }` buys **0 gas** on solc 0.8.34 (measured); `string calldata` on the admin `setDocument` is **49 gas worse** than `memory` (measured); and the duplicated ERC-2771 context overrides **cannot** be extracted into a shared module — C3 linearization forces each deployment to re-state them, proven by compiler error. +- **Style pass across `src/` and `script/` — behaviour-preserving.** Brought the sources in line with the Solidity style guide: functions reordered by visibility group (external → public → internal, `view`/`pure` last within each), so the `_authorize*` hooks and the ERC-2771 context overrides now follow the public API instead of preceding it; every brace-less global import replaced by a named one (which required adding the previously implicit `Context` and `AccessControl` imports, since a named import no longer re-exports a dependency's own imports); and NatSpec completed with a `@param` per argument and a `@return` per return value. No signature, visibility, body or storage layout changed — verified by an unchanged per-contract function set, a clean `forge build`, and 72/72 tests passing. +- **Source pragma raised `^0.8.20` → `^0.8.24`** across `src/`, `script/` and `test/`. This is a correction, not a new restriction: `^0.8.20` had become an over-promise, advertising a range the sources could not actually compile in. OpenZeppelin's `AccessControlEnumerable.sol` and `EnumerableSet.sol` are `^0.8.24`, and CMTAT `v3.3.0-rc3` moved `draft-IERC1643.sol` to `^0.8.24` as well, so every contract in `src/` now transitively requires it — `forge build --use 0.8.23` fails to resolve a compiler. `0.8.24` is the real `src/` floor; the full project including the CMTAT-importing tests needs `0.8.27`, because `require(cond, CustomError())` is restricted to the via-ir pipeline before then. Deployed bytecode is unaffected — the pinned compiler is still `0.8.34`. - **`IERC1643` (CMTAT v3) breaking changes** - - `getDocument` keeps returning `(string uri, bytes32 documentHash, uint256 lastModified)` — the - flat ERC-1643 ABI — on **both** overloads, `getDocument(bytes32)` and - `getDocument(address subject, bytes32)`. CMTAT `v3.3.0-rc1` briefly replaced this with a - `Document` struct and `v3.3.0-rc2` reverted it; this engine follows rc2/rc3, so relative to - `v0.3.0` the external shape is unchanged. - - The distinction is worth recording because it is invisible to interface detection: return types - are not part of a function signature, so both shapes share the same selectors and the same - `type(IERC1643).interfaceId` (`0xecfecec8`). A consumer built from the specification ABI decodes - a struct return as garbage *without reverting* — `uri` becomes binary junk, `documentHash` - becomes `0x…60`, and `lastModified` becomes the real hash as a `uint256`. `getDocument` is now - covered by `testGetDocumentReturnsFlatErc1643Abi`, which inspects the returndata directly since - ERC-165 structurally cannot. - - The `Document` struct and the `DocumentUpdated`/`DocumentRemoved` events are now provided by - `IERC1643`; the duplicate local declarations were removed from `DocumentEngineInvariant`. The - struct is retained internally for storage only. - - `ERC1643InvalidName()` / `ERC1643MissingDocument()` are likewise declared by `IERC1643` as of - CMTAT `v3.3.0-rc2` and are **not** re-declared here. The multi-subject draft requires a contract - implementing both interfaces to obtain each error exactly once ("MUST NOT declare them twice"), - and re-declaring is a compile error. Selectors, and hence revert data, are unchanged. The same - principle was applied to every other error: `MultiDocumentInvalidSubject()` moved to - `IERC1643MultiDocument` and `TokenBindingInvalidToken()` is declared on `ITokenBinding`, so an - ABI generated from an interface carries its errors. `DocumentEngineInvariant` now holds only - `InvalidInputLength` and `AdminWithAddressZeroNotAllowed`, which no interface defines. - - Import path moved: `CMTAT/interfaces/engine/draft-IERC1643.sol` → - `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. - -- **`ERC1643InvalidSubject()` renamed to `MultiDocumentInvalidSubject()`** and moved from - `DocumentEngineInvariant` to `IERC1643MultiDocument`, matching the multi-subject draft. **This - changes the error selector**, so integrators decoding this revert must update. - - The draft's rule is that an error is prefixed by the proposal that *defines* its condition, not by - the one it sits next to. The null-`subject` condition cannot arise in ERC-1643 at all — its - `setDocument` has no `subject` argument, so the subject is implicitly the contract itself, which - is never the null address — so borrowing the `ERC1643` prefix named the error after a standard in - which it is unreachable. The two genuinely-shared errors keep their prefix for the opposite - reason. -- **Token binding rejects `address(0)` and is idempotent.** `bindToken` / `unbindToken` now revert - `TokenBindingInvalidToken()` on the null address — which can never call the engine, so binding it - granted nothing while still emitting an event indexers key on — and write plus emit - `TokenBindingSet` **only when the binding actually changes**. A repeated call still succeeds, - since the caller's intent already holds, but emits nothing, so every event in the log is a real - transition and an indexer never has to de-duplicate. + - `getDocument` keeps returning `(string uri, bytes32 documentHash, uint256 lastModified)` — the flat ERC-1643 ABI — on **both** overloads, `getDocument(bytes32)` and `getDocument(address subject, bytes32)`. CMTAT `v3.3.0-rc1` briefly replaced this with a `Document` struct and `v3.3.0-rc2` reverted it; this engine follows rc2/rc3, so relative to `v0.3.0` the external shape is unchanged. + + The distinction is worth recording because it is invisible to interface detection: return types are not part of a function signature, so both shapes share the same selectors and the same `type(IERC1643).interfaceId` (`0xecfecec8`). A consumer built from the specification ABI decodes a struct return as garbage *without reverting* — `uri` becomes binary junk, `documentHash` becomes `0x…60`, and `lastModified` becomes the real hash as a `uint256`. `getDocument` is now covered by `testGetDocumentReturnsFlatErc1643Abi`, which inspects the returndata directly since ERC-165 structurally cannot. + - The `Document` struct and the `DocumentUpdated`/`DocumentRemoved` events are now provided by `IERC1643`; the duplicate local declarations were removed from `DocumentEngineInvariant`. The struct is retained internally for storage only. + - `ERC1643InvalidName()` / `ERC1643MissingDocument()` are likewise declared by `IERC1643` as of CMTAT `v3.3.0-rc2` and are **not** re-declared here. The multi-subject draft requires a contract implementing both interfaces to obtain each error exactly once ("MUST NOT declare them twice"), and re-declaring is a compile error. Selectors, and hence revert data, are unchanged. The same principle was applied to every other error: `MultiDocumentInvalidSubject()` moved to `IERC1643MultiDocument` and `TokenBindingInvalidToken()` is declared on `ITokenBinding`, so an ABI generated from an interface carries its errors. `DocumentEngineInvariant` now holds only `InvalidInputLength` and `AdminWithAddressZeroNotAllowed`, which no interface defines. + - Import path moved: `CMTAT/interfaces/engine/draft-IERC1643.sol` → `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. + +- **`ERC1643InvalidSubject()` renamed to `MultiDocumentInvalidSubject()`** and moved from `DocumentEngineInvariant` to `IERC1643MultiDocument`, matching the multi-subject draft. **This changes the error selector**, so integrators decoding this revert must update. + + The draft's rule is that an error is prefixed by the proposal that *defines* its condition, not by the one it sits next to. The null-`subject` condition cannot arise in ERC-1643 at all — its `setDocument` has no `subject` argument, so the subject is implicitly the contract itself, which is never the null address — so borrowing the `ERC1643` prefix named the error after a standard in which it is unreachable. The two genuinely-shared errors keep their prefix for the opposite reason. +- **Token binding rejects `address(0)` and is idempotent.** `bindToken` / `unbindToken` now revert `TokenBindingInvalidToken()` on the null address — which can never call the engine, so binding it granted nothing while still emitting an event indexers key on — and write plus emit `TokenBindingSet` **only when the binding actually changes**. A repeated call still succeeds, since the caller's intent already holds, but emits nothing, so every event in the log is a real transition and an indexer never has to de-duplicate. ### Added -- **Bound-token document management**: implement the now-mandatory - `IERC1643.setDocument(name, uri, hash)` and `removeDocument(name)`, gated by the `onlyBoundToken` - modifier and scoped to the caller (`_msgSender()`) own namespace. A token bound with - `bindToken(token)` (see the shared binding module below) manages its own documents and can never - affect another contract's documents. The admin overloads (explicit `address`, - `DOCUMENT_MANAGER_ROLE`) are unchanged, so both systems work side by side. (RuleEngine's - `ERC3643ComplianceExtendedModule` was evaluated for the binding but intentionally not reused — see - the README.) -- **Optional multi-token events**: alongside the standard `IERC1643` events, the engine now also - emits `DocumentUpdatedForContract` / `DocumentRemovedForContract`, which carry the `smartContract` - (token) address so off-chain indexers can tell which contract a document belongs to during - multi-contract operations. See - [`erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md) - for the proposed optional standard extension. -- **Flexible access control (CMTAT / RuleEngine pattern)**: the restricted functions use the - `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to overridable - `internal virtual` authorization hooks `_authorizeDocumentManagement()` / - `_authorizeBoundTokenDocumentManagement()`. Each deployment implements the admin hook - (`DOCUMENT_MANAGER_ROLE` or `owner`); the bound-token hook is implemented once by - `TokenBindingModule` (the shared allowlist). This separates the document-management implementation - from the authorization logic. -- **Split into a base contract and a deployment contract** (CMTAT module/deployment pattern): the - document-management logic and storage now live in the new abstract `DocumentEngineBase` (with - abstract `_authorize*` hooks), while `DocumentEngine` is the deployment contract that defines the - access control (`AccessControl`, the concrete hooks and `hasRole`) and the ERC-2771 wiring. The - deployable `DocumentEngine` API and behavior are unchanged. -- **Version module implementing ERC-8303**: the version is now exposed through a dedicated - `VersionModule` (`src/modules/VersionModule.sol`) implementing the `IERC8303` interface - (`src/interfaces/IERC8303.sol`). It adds a standard `version()` view function (in addition to the - existing public `VERSION` constant) and advertises ERC-8303 via ERC-165 - (`supportsInterface(0x54fd4d50) == true`). `DocumentEngine` combines the module's - `supportsInterface` with the access-control base. -- **Second deployment `DocumentEngineOwnable`** (`src/DocumentEngineOwnable.sol`): an alternative - deployment that uses OpenZeppelin `Ownable2Step` (single owner, two-step transfer) instead of - role-based access control, reusing the same `DocumentEngineBase` logic and the shared - `TokenBindingModule`. Both document management and token binding are restricted to the `owner`. +- **Bound-token document management**: implement the now-mandatory `IERC1643.setDocument(name, uri, hash)` and `removeDocument(name)`, gated by the `onlyBoundToken` modifier and scoped to the caller (`_msgSender()`) own namespace. A token bound with `bindToken(token)` (see the shared binding module below) manages its own documents and can never affect another contract's documents. The admin overloads (explicit `address`, `DOCUMENT_MANAGER_ROLE`) are unchanged, so both systems work side by side. (RuleEngine's `ERC3643ComplianceExtendedModule` was evaluated for the binding but intentionally not reused — see the README.) +- **Optional multi-token events**: alongside the standard `IERC1643` events, the engine now also emits `DocumentUpdatedForContract` / `DocumentRemovedForContract`, which carry the `smartContract` (token) address so off-chain indexers can tell which contract a document belongs to during multi-contract operations. See [`erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md) for the proposed optional standard extension. +- **Flexible access control (CMTAT / RuleEngine pattern)**: the restricted functions use the `onlyDocumentManager` / `onlyBoundToken` modifiers, which delegate to overridable `internal virtual` authorization hooks `_authorizeDocumentManagement()` / `_authorizeBoundTokenDocumentManagement()`. Each deployment implements the admin hook (`DOCUMENT_MANAGER_ROLE` or `owner`); the bound-token hook is implemented once by `TokenBindingModule` (the shared allowlist). This separates the document-management implementation from the authorization logic. +- **Split into a base contract and a deployment contract** (CMTAT module/deployment pattern): the document-management logic and storage now live in the new abstract `DocumentEngineBase` (with abstract `_authorize*` hooks), while `DocumentEngine` is the deployment contract that defines the access control (`AccessControl`, the concrete hooks and `hasRole`) and the ERC-2771 wiring. The deployable `DocumentEngine` API and behavior are unchanged. +- **Version module implementing ERC-8303**: the version is now exposed through a dedicated `VersionModule` (`src/modules/VersionModule.sol`) implementing the `IERC8303` interface (`src/interfaces/IERC8303.sol`). It adds a standard `version()` view function (in addition to the existing public `VERSION` constant) and advertises ERC-8303 via ERC-165 (`supportsInterface(0x54fd4d50) == true`). `DocumentEngine` combines the module's `supportsInterface` with the access-control base. +- **Second deployment `DocumentEngineOwnable`** (`src/DocumentEngineOwnable.sol`): an alternative deployment that uses OpenZeppelin `Ownable2Step` (single owner, two-step transfer) instead of role-based access control, reusing the same `DocumentEngineBase` logic and the shared `TokenBindingModule`. Both document management and token binding are restricted to the `owner`. ### Changed (access control) -- `DocumentEngine` now inherits **`AccessControlEnumerable`** instead of `AccessControl`, adding - on-chain enumeration of role members (`getRoleMember`, `getRoleMemberCount`) and advertising - `IAccessControlEnumerable` via ERC-165. Default authorization behavior is unchanged. -- Moved the `DOCUMENT_MANAGER_ROLE` constant out of the shared `DocumentEngineInvariant` and into - the role-based `DocumentEngine`, so `DocumentEngineInvariant` (and the `DocumentEngineOwnable` - deployment) no longer carry access-control-specific constants. The invariant now holds only the - shared errors. +- `DocumentEngine` now inherits **`AccessControlEnumerable`** instead of `AccessControl`, adding on-chain enumeration of role members (`getRoleMember`, `getRoleMemberCount`) and advertising `IAccessControlEnumerable` via ERC-165. Default authorization behavior is unchanged. +- Moved the `DOCUMENT_MANAGER_ROLE` constant out of the shared `DocumentEngineInvariant` and into the role-based `DocumentEngine`, so `DocumentEngineInvariant` (and the `DocumentEngineOwnable` deployment) no longer carry access-control-specific constants. The invariant now holds only the shared errors. ### Fixed (ERC-1643 conformance) -Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/erc-1643.md) (which -now folds in the multi-token extension and the emission-responsibility rules): - -- **Emission responsibility.** As a shared, multi-token manager the engine now emits **only** the - address-carrying extension events and **no longer** emits the base `DocumentUpdated` / - `DocumentRemoved` events (the spec's `MUST NOT` for a shared manager — those events carry no - `subject` and belong on the token contract). -- **Extension events/interface.** Renamed the multi-token events to the standard - `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` (parameter `subject`), and introduced - the `IERC1643MultiDocument` interface (`src/interfaces/IERC1643MultiDocument.sol`) that the base - now implements — the address-scoped `getDocument` / `getAllDocuments` / `setDocument` / - `removeDocument`. -- **Input validation.** `setDocument` now reverts `ERC1643InvalidName()` when `name == bytes32(0)` - and `MultiDocumentInvalidSubject()` when `subject == address(0)` (the multi-subject draft's - null-namespace guard); `removeDocument` now reverts `ERC1643MissingDocument()` for a non-existent - document (previously it silently emitted a spurious removal event). See - [`erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md) - for the corresponding multi-subject draft. -- **ERC-165 discovery.** `supportsInterface` now returns `true` for `type(IERC1643).interfaceId`, - `type(IERC1643MultiDocument).interfaceId` and `type(ITokenBinding).interfaceId` (both - deployments). - - The base id is advertised because the engine implements the base single-argument functions, and - because a **token** uses it: before wiring itself to the engine with `setDocumentEngine(engine)`, - or before forwarding `setDocument(name, uri, hash)`, it can confirm through ERC-165 that those - endpoints exist. It does **not** mean documents should be read from the engine's address — the - base functions are `_msgSender()`-scoped, so a third-party read returns the caller's own empty - namespace. Documented in the README and asserted by - `testBaseERC1643IsAdvertisedButReadsAreCallerScoped`. +Aligned the implementation with the updated [ERC-1643](./doc/ERCSpecification/erc-1643.md) (which now folds in the multi-token extension and the emission-responsibility rules): + +- **Emission responsibility.** As a shared, multi-token manager the engine now emits **only** the address-carrying extension events and **no longer** emits the base `DocumentUpdated` / `DocumentRemoved` events (the spec's `MUST NOT` for a shared manager — those events carry no `subject` and belong on the token contract). +- **Extension events/interface.** Renamed the multi-token events to the standard `DocumentUpdatedForSubject` / `DocumentRemovedForSubject` (parameter `subject`), and introduced the `IERC1643MultiDocument` interface (`src/interfaces/IERC1643MultiDocument.sol`) that the base now implements — the address-scoped `getDocument` / `getAllDocuments` / `setDocument` / `removeDocument`. +- **Input validation.** `setDocument` now reverts `ERC1643InvalidName()` when `name == bytes32(0)` and `MultiDocumentInvalidSubject()` when `subject == address(0)` (the multi-subject draft's null-namespace guard); `removeDocument` now reverts `ERC1643MissingDocument()` for a non-existent document (previously it silently emitted a spurious removal event). See [`erc-draft_multi_document_management.md`](./doc/ERCSpecification/erc-draft_multi_document_management.md) for the corresponding multi-subject draft. +- **ERC-165 discovery.** `supportsInterface` now returns `true` for `type(IERC1643).interfaceId`, `type(IERC1643MultiDocument).interfaceId` and `type(ITokenBinding).interfaceId` (both deployments). + + The base id is advertised because the engine implements the base single-argument functions, and because a **token** uses it: before wiring itself to the engine with `setDocumentEngine(engine)`, or before forwarding `setDocument(name, uri, hash)`, it can confirm through ERC-165 that those endpoints exist. It does **not** mean documents should be read from the engine's address — the base functions are `_msgSender()`-scoped, so a third-party read returns the caller's own empty namespace. Documented in the README and asserted by `testBaseERC1643IsAdvertisedButReadsAreCallerScoped`. ### Added (token binding) -- **Shared `ITokenBinding` interface + `TokenBindingModule`.** `bindToken(token)` / - `unbindToken(token)` / `isTokenBound(token)` + `TokenBindingSet` event - (`src/interfaces/ITokenBinding.sol`), implemented once for both deployments by - `src/modules/TokenBindingModule.sol` — a single **allowlist**, not a role. Both deployments now - share the exact same binding mechanism (same functions, event, and `NotBoundToken` revert on an - unbound write) and advertise `type(ITokenBinding).interfaceId` via ERC-165. The role deployment - **no longer uses `TOKEN_CONTRACT_ROLE`** (removed) — binding is authorized by the - document-management hook (`DOCUMENT_MANAGER_ROLE`, or the `owner` in `DocumentEngineOwnable`). +- **Shared `ITokenBinding` interface + `TokenBindingModule`.** `bindToken(token)` / `unbindToken(token)` / `isTokenBound(token)` + `TokenBindingSet` event (`src/interfaces/ITokenBinding.sol`), implemented once for both deployments by `src/modules/TokenBindingModule.sol` — a single **allowlist**, not a role. Both deployments now share the exact same binding mechanism (same functions, event, and `NotBoundToken` revert on an unbound write) and advertise `type(ITokenBinding).interfaceId` via ERC-165. The role deployment **no longer uses `TOKEN_CONTRACT_ROLE`** (removed) — binding is authorized by the document-management hook (`DOCUMENT_MANAGER_ROLE`, or the `owner` in `DocumentEngineOwnable`). ### Notes / bottlenecks -- **Subject-side emission is CMTAT `v3.3.0-rc2` or later.** rc2 made `DocumentEngineModule` re-emit - the standard `DocumentUpdated` / `DocumentRemoved` on the **token's own address** after forwarding - to the engine, and revert with `CMTAT_DocumentEngineModule_NoDocumentEngine` when no engine is - set. Combined with this engine emitting only the address-carrying `*ForSubject` events, the - subject-initiated call topology is fully conformant with the multi-subject draft's *Emission - Responsibility* rules. The **admin path remains non-conformant by construction** — a write sent - straight to the engine has no execution point in the subject, so the subject emits nothing. See - `OPEN-2` in [`AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md). -- Open items are tracked under *Known open items* in - [`AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md): the most severe is admin-path call topology - (`OPEN-2`); also authorization granularity (`OPEN-1`) and enumeration cost (`OPEN-4`). -- CMTAT v3 no longer ships a *standalone* token that consumes an external document engine through - its constructor; the standard token stores documents on-chain (`DocumentERC1643Module`). - External-engine integration now goes through CMTAT's `DocumentEngineModule` (`setDocumentEngine`). - The test suite was updated to exercise this real integration path via a minimal token built on - `DocumentEngineModule`. +- **Subject-side emission is CMTAT `v3.3.0-rc2` or later.** rc2 made `DocumentEngineModule` re-emit the standard `DocumentUpdated` / `DocumentRemoved` on the **token's own address** after forwarding to the engine, and revert with `CMTAT_DocumentEngineModule_NoDocumentEngine` when no engine is set. Combined with this engine emitting only the address-carrying `*ForSubject` events, the subject-initiated call topology is fully conformant with the multi-subject draft's *Emission Responsibility* rules. The **admin path remains non-conformant by construction** — a write sent straight to the engine has no execution point in the subject, so the subject emits nothing. See `OPEN-2` in [`AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md). +- Open items are tracked under *Known open items* in [`AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md): the most severe is admin-path call topology (`OPEN-2`); also authorization granularity (`OPEN-1`) and enumeration cost (`OPEN-4`). +- CMTAT v3 no longer ships a *standalone* token that consumes an external document engine through its constructor; the standard token stores documents on-chain (`DocumentERC1643Module`). External-engine integration now goes through CMTAT's `DocumentEngineModule` (`setDocumentEngine`). The test suite was updated to exercise this real integration path via a minimal token built on `DocumentEngineModule`. ## v0.3.0 diff --git a/CLAUDE.md b/CLAUDE.md index 61e0a5e..3ae4d61 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -162,7 +162,7 @@ Other important files: - `README.md` — **short** entry point only: what the engine is, quick start, the two management paths, the CMTAT wiring, the two integrator caveats, deploy. Keep it short; new prose belongs in the full document. -- `doc/README.md` — the full documentation (Surya schema, ERC-165 rationale, version +- `doc/README.md` — the specification / full reference (Surya schema, ERC-165 rationale, version compatibility matrix, tooling). This is where the old root README moved. - `doc/img/` — PlantUML **sources** (`*.puml`) plus their rendered `*.png`. Five diagrams, split by audience: `cmtat-write-simple` / `cmtat-read-simple` are the **short** pair, used in *both* diff --git a/README.md b/README.md index e2802fc..c30d03a 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,10 @@ # DocumentEngine (ERC-1643) -> This project has not been audited yet, please use at your own risk. For any questions, please -> contact [admin@cmta.ch](mailto:admin@cmta.ch). +> This project has not been audited yet, please use at your own risk. For any questions, please contact [admin@cmta.ch](mailto:admin@cmta.ch). -A standalone contract that stores **[ERC-1643](https://github.com/ethereum/EIPs/issues/1643) -documents on-chain on behalf of other contracts** — typically [CMTAT](https://github.com/CMTA/CMTAT) -tokens. One engine serves a whole fleet: each subject gets its own namespace, keyed by its address, -and can never reach another's. +A standalone contract that stores **[ERC-1643](https://github.com/ethereum/EIPs/issues/1643) documents on-chain on behalf of other contracts** — typically [CMTAT](https://github.com/CMTA/CMTAT) tokens. One engine serves a whole fleet: each subject gets its own namespace, keyed by its address, and can never reach another's. -A document is `{ string uri, bytes32 documentHash, uint256 lastModified }`, addressed by a `bytes32` -name. +A document is `{ string uri, bytes32 documentHash, uint256 lastModified }`, addressed by a `bytes32` name. Why use an external engine rather than storing documents in the token: @@ -17,7 +12,7 @@ Why use an external engine rather than storing documents in the token: - lets one operator manage documents for many tokens; - documents can be updated without touching the token. -**Full documentation: [`doc/README.md`](./doc/README.md).** +**Specification and full reference: [`doc/README.md`](./doc/README.md).** ## Quick start @@ -28,8 +23,7 @@ forge build forge test ``` -Requires [Foundry](https://getfoundry.sh) and Solidity `0.8.34` (`evm_version = prague`). Sources -declare `pragma ^0.8.24`; building the tests needs `≥ 0.8.27`. +Requires [Foundry](https://getfoundry.sh) and Solidity `0.8.34` (`evm_version = prague`). Sources declare `pragma ^0.8.24`; building the tests needs `≥ 0.8.27`. ## Two ways to manage documents @@ -42,8 +36,7 @@ documentEngine.setDocument(address(token), name, uri, documentHash); documentEngine.removeDocument(address(token), name); ``` -**Bound-token path** — a *bound* token manages its **own** documents through the standard -single-argument ERC-1643 functions (`msg.sender` is the subject): +**Bound-token path** — a *bound* token manages its **own** documents through the standard single-argument ERC-1643 functions (`msg.sender` is the subject): ```solidity documentEngine.bindToken(address(token)); // once, by the document manager @@ -60,15 +53,11 @@ Binding is a single allowlist shared by both deployments — **not** a role. | `DocumentEngine` | `AccessControlEnumerable` | `DOCUMENT_MANAGER_ROLE` | | `DocumentEngineOwnable` | `Ownable2Step` | `owner` | -They share all the logic (`DocumentEngineBase`, `TokenBindingModule`, `VersionModule`) and differ -only in *who* is authorized. Authorization goes through an overridable `internal virtual` hook, so a -subclass changes who may write without touching the management functions. +They share all the logic (`DocumentEngineBase`, `TokenBindingModule`, `VersionModule`) and differ only in *who* is authorized. Authorization goes through an overridable `internal virtual` hook, so a subclass changes who may write without touching the management functions. ## Using it with a CMTAT token -Two independent steps, and it is easy to do only one: `bindToken` on the **engine** authorises the -token, `setDocumentEngine` on the **token** tells it where to forward. Bind without wiring and the -token has nowhere to send; wire without binding and the forwarded call reverts `NotBoundToken`. +Two independent steps, and it is easy to do only one: `bindToken` on the **engine** authorises the token, `setDocumentEngine` on the **token** tells it where to forward. Bind without wiring and the token has nowhere to send; wire without binding and the forwarded call reverts `NotBoundToken`. ```solidity documentEngine.bindToken(address(token)); // engine's document manager @@ -85,21 +74,13 @@ token.setDocument(bytes32("prospectus"), "ipfs://...", keccak256(bytes(content)) ![Reading a document from a CMTAT token or the engine](./doc/img/cmtat-read-simple.png) -For the full flow — the wiring steps, every revert branch, and the admin path — see -[the detailed sequence](./doc/README.md#integration-with-cmtat) in the documentation. +For the full flow — the wiring steps, every revert branch, and the admin path — see [the detailed sequence](./doc/README.md#integration-with-cmtat) in the documentation. ## Two things integrators must know -**Read through the subject, not the engine.** As the read diagram shows, the single-argument -`getDocument(name)` is `msg.sender`-scoped, so a third party calling it on the engine reads *its -own* — empty — namespace, with no revert. Read through the token, or use the address-scoped -`getDocument(subject, name)`. +**Read through the subject, not the engine.** As the read diagram shows, the single-argument `getDocument(name)` is `msg.sender`-scoped, so a third party calling it on the engine reads *its own* — empty — namespace, with no revert. Read through the token, or use the address-scoped `getDocument(subject, name)`. -**The admin path emits nothing on the subject.** A write sent straight to the engine -(`setDocument(subject, …)`, rather than through the token as above) has no execution point in the -token, so only the engine's `DocumentUpdatedForSubject` fires. When consumers watch the token's -address, use the bound-token path. Tracked as `OPEN-2` in -[`doc/audits/AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md). +**The admin path emits nothing on the subject.** A write sent straight to the engine (`setDocument(subject, …)`, rather than through the token as above) has no execution point in the token, so only the engine's `DocumentUpdatedForSubject` fires. When consumers watch the token's address, use the bound-token path. Tracked as `OPEN-2` in [`doc/audits/AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md). ## Deploy @@ -113,25 +94,21 @@ DOCUMENT_ENGINE_OWNER=0x… DOCUMENT_ENGINE_FORWARDER=0x… \ forge script script/DeployDocumentEngineOwnable.s.sol --rpc-url $RPC_URL --broadcast ``` -The forwarder enables ERC-2771 gasless calls and is **immutable**; pass `address(0)` to disable. Use -a keystore or hardware wallet for real deployments, not a raw private key. +The forwarder enables ERC-2771 gasless calls and is **immutable**; pass `address(0)` to disable. Use a keystore or hardware wallet for real deployments, not a raw private key. ## More | | | | --- | --- | -| Full documentation | [`doc/README.md`](./doc/README.md) | +| Specification / full reference | [`doc/README.md`](./doc/README.md) | | Security overview & open items | [`doc/audits/AUDIT_OVERVIEW.md`](./doc/audits/AUDIT_OVERVIEW.md) | | Static analysis & code-quality reports | [`doc/audits/tools/`](./doc/audits/tools) | | Release history | [`CHANGELOG.md`](./CHANGELOG.md) | | Reporting a vulnerability | [`SECURITY.md`](./SECURITY.md) | | Diagrams (Surya, PlantUML) | [`doc/surya/`](./doc/surya), [`doc/img/`](./doc/img) | -Current version `0.4.0`, built against CMTAT `v3.3.0-rc3` and OpenZeppelin `v5.7.0` — see the -[version compatibility matrix](./doc/README.md#version-compatibility), since this engine is -supported only against the CMTAT release it was built for. +Current version `0.4.0`, built against CMTAT `v3.3.0-rc3` and OpenZeppelin `v5.7.0` — see the [version compatibility matrix](./doc/README.md#version-compatibility), since this engine is supported only against the CMTAT release it was built for. ## Intellectual property -The code is copyright (c) Capital Market and Technology Association, 2018-2024, and is released -under [Mozilla Public License 2.0](https://github.com/CMTA/CMTAT/blob/master/LICENSE.md). +The code is copyright (c) Capital Market and Technology Association, 2018-2024, and is released under [Mozilla Public License 2.0](https://github.com/CMTA/CMTAT/blob/master/LICENSE.md). diff --git a/doc/README.md b/doc/README.md index df5c16b..23ceac7 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,15 +1,10 @@ -# DocumentEngine — full documentation (ERC-1643) +# DocumentEngine (ERC-1643) - Specification -> This project has not been audited yet, please use at your own risk. For any questions, please -> contact [admin@cmta.ch](mailto:admin@cmta.ch). +> This project has not been audited yet, please use at your own risk. For any questions, please contact [admin@cmta.ch](mailto:admin@cmta.ch). -The `DocumentEngine` is an external contract to manage documents through -[*ERC-1643*](https://github.com/ethereum/EIPs/issues/1643), a proposed standard for managing -documents on-chain. [ERC-1400](https://github.com/ethereum/eips/issues/1411) from Polymath builds on -it. +The `DocumentEngine` is an external contract to manage documents through [*ERC-1643*](https://github.com/ethereum/EIPs/issues/1643), a proposed standard for managing documents on-chain. [ERC-1400](https://github.com/ethereum/eips/issues/1411) from Polymath builds on it. -The DocumentEngine is meant to be used by other smart contracts, e.g. a CMTAT token, to store -documents on their behalf. +The DocumentEngine is meant to be used by other smart contracts, e.g. a CMTAT token, to store documents on their behalf. The ERC-1643 defines a document with three attributes: @@ -17,9 +12,7 @@ The ERC-1643 defines a document with three attributes: - A generic URI (represented as a `string`) that could point to a website or other document portal. - The hash of the document contents associated with it on-chain. -A smart contract needs only to read documents from this standard through the interface -[IERC1643](../lib/CMTAT/contracts/interfaces/tokenization/draft-IERC1643.sol) to get the documents -from the documentEngine: +A smart contract needs only to read documents from this standard through the interface [IERC1643](../lib/CMTAT/contracts/interfaces/tokenization/draft-IERC1643.sol) to get the documents from the documentEngine: ```solidity interface IERC1643 { @@ -36,13 +29,7 @@ interface IERC1643 { } ``` -> **Note — `getDocument` returns flat values.** CMTAT `v3.3.0-rc1` briefly returned a `Document` -> struct here; `v3.3.0-rc2` restored the three flat return values mandated by the ERC-1643 ABI, and -> this engine follows. Return types are not part of a function signature, so both shapes share the -> same selector and the same `type(IERC1643).interfaceId`: a struct return is undetectable through -> ERC-165, and a consumer built from the specification ABI decodes it as garbage without reverting. -> The `Document` struct is kept internally for storage only, and -> `testGetDocumentReturnsFlatErc1643Abi` pins the wire format. +> **Note — `getDocument` returns flat values.** CMTAT `v3.3.0-rc1` briefly returned a `Document` struct here; `v3.3.0-rc2` restored the three flat return values mandated by the ERC-1643 ABI, and this engine follows. Return types are not part of a function signature, so both shapes share the same selector and the same `type(IERC1643).interfaceId`: a struct return is undetectable through ERC-165, and a consumer built from the specification ABI decodes it as garbage without reverting. The `Document` struct is kept internally for storage only, and `testGetDocumentReturnsFlatErc1643Abi` pins the wire format. Using an external contract for your smart contract provides two advantages: @@ -53,9 +40,7 @@ Using an external contract for your smart contract provides two advantages: The engine supports **two management paths** at the same time: -**1. Admin path (`DOCUMENT_MANAGER_ROLE`).** Since the engine manages documents for several -different smart contracts, the admin functions take one supplementary `address smartContract` -argument compared to the ERC-1643: +**1. Admin path (`DOCUMENT_MANAGER_ROLE`).** Since the engine manages documents for several different smart contracts, the admin functions take one supplementary `address smartContract` argument compared to the ERC-1643: ```solidity // DocumentEngine (admin overloads) @@ -63,23 +48,15 @@ function setDocument(address smartContract, bytes32 name_, string memory uri_, b function removeDocument(address smartContract, bytes32 name_) external; ``` -**2. Bound-token path.** This implements the standard, single-argument ERC-1643 functions. A token -is *bound* to the engine through the shared **`ITokenBinding`** surface — identical across both -deployments, so integrators bind/query a token the same way regardless of the access-control model: +**2. Bound-token path.** This implements the standard, single-argument ERC-1643 functions. A token is *bound* to the engine through the shared **`ITokenBinding`** surface — identical across both deployments, so integrators bind/query a token the same way regardless of the access-control model: ```solidity documentEngine.bindToken(address(token)); // also: unbindToken(token), isTokenBound(token) ``` -Both deployments share the exact same binding mechanism — a single allowlist in `TokenBindingModule` -(`src/modules/TokenBindingModule.sol`), **not** a role. They expose the same `bindToken` / -`unbindToken` / `isTokenBound` functions, emit the same `TokenBindingSet` event, and revert with the -same `NotBoundToken` error when a non-bound caller attempts a write. The only difference is *who* -may bind: whoever may manage documents in that deployment (the `DOCUMENT_MANAGER_ROLE` holder, or -the `owner`), since binding is authorized by the same document-management hook. +Both deployments share the exact same binding mechanism — a single allowlist in `TokenBindingModule` (`src/modules/TokenBindingModule.sol`), **not** a role. They expose the same `bindToken` / `unbindToken` / `isTokenBound` functions, emit the same `TokenBindingSet` event, and revert with the same `NotBoundToken` error when a non-bound caller attempts a write. The only difference is *who* may bind: whoever may manage documents in that deployment (the `DOCUMENT_MANAGER_ROLE` holder, or the `owner`), since binding is authorized by the same document-management hook. -Once bound, the token manages its **own** documents (`msg.sender` is the token); it can never affect -another contract's documents: +Once bound, the token manages its **own** documents (`msg.sender` is the token); it can never affect another contract's documents: ```solidity // DocumentEngine (standard ERC-1643, scoped to msg.sender) @@ -87,23 +64,14 @@ function setDocument(bytes32 name_, string calldata uri_, bytes32 documentHash_) function removeDocument(bytes32 name_) external; ``` -> This mirrors the RuleEngine *binding* pattern without reusing its -> `ERC3643ComplianceExtendedModule` — see -> [Why not reuse RuleEngine's ERC-3643 compliance module?](#why-not-reuse-ruleengines-erc-3643-compliance-module) -> below. +> This mirrors the RuleEngine *binding* pattern without reusing its `ERC3643ComplianceExtendedModule` — see [Why not reuse RuleEngine's ERC-3643 compliance module?](#why-not-reuse-ruleengines-erc-3643-compliance-module) below. ### Flexible access control -Following the CMTAT / [RuleEngine](https://github.com/CMTA/RuleEngine) pattern, the restricted -functions do not hardcode a check. They carry a **modifier** (`onlyDocumentManager` / -`onlyBoundToken`) that delegates to an **overridable `internal virtual` authorization hook**: +Following the CMTAT / [RuleEngine](https://github.com/CMTA/RuleEngine) pattern, the restricted functions do not hardcode a check. They carry a **modifier** (`onlyDocumentManager` / `onlyBoundToken`) that delegates to an **overridable `internal virtual` authorization hook**: -- the **admin path** delegates to `_authorizeDocumentManagement()`, the one hook each deployment - implements (`_checkRole(DOCUMENT_MANAGER_ROLE)` for `DocumentEngine`, `_checkOwner()` for - `DocumentEngineOwnable`); -- the **bound-token path** delegates to `_authorizeBoundTokenDocumentManagement()`, which - `TokenBindingModule` implements once for both deployments (it checks the shared binding - allowlist). +- the **admin path** delegates to `_authorizeDocumentManagement()`, the one hook each deployment implements (`_checkRole(DOCUMENT_MANAGER_ROLE)` for `DocumentEngine`, `_checkOwner()` for `DocumentEngineOwnable`); +- the **bound-token path** delegates to `_authorizeBoundTokenDocumentManagement()`, which `TokenBindingModule` implements once for both deployments (it checks the shared binding allowlist). ```solidity // implemented per deployment (the only access-control hook they supply) @@ -117,19 +85,13 @@ function _authorizeBoundTokenDocumentManagement() internal view virtual override } ``` -This separates the document-management implementation from the authorization logic: a subclass -changes *who* is authorized by overriding the hook, never by touching the management functions. +This separates the document-management implementation from the authorization logic: a subclass changes *who* is authorized by overriding the hook, never by touching the management functions. ### Why not reuse RuleEngine's ERC-3643 compliance module? -CMTA's [RuleEngine](https://github.com/CMTA/RuleEngine) (v3) ships an -`ERC3643ComplianceExtendedModule` that offers a ready-made token-binding registry (`bindToken` / -`unbindToken` / `isTokenBound` / `getTokenBounds`). It is tempting to reuse it for the bound-token -path, but we deliberately do **not**, because that module is an **`IERC3643Compliance`** — a -*transfer-compliance* contract. +CMTA's [RuleEngine](https://github.com/CMTA/RuleEngine) (v3) ships an `ERC3643ComplianceExtendedModule` that offers a ready-made token-binding registry (`bindToken` / `unbindToken` / `isTokenBound` / `getTokenBounds`). It is tempting to reuse it for the bound-token path, but we deliberately do **not**, because that module is an **`IERC3643Compliance`** — a *transfer-compliance* contract. -Inheriting it would force the DocumentEngine to also implement the ERC-3643 transfer-compliance -callbacks that come with that interface: +Inheriting it would force the DocumentEngine to also implement the ERC-3643 transfer-compliance callbacks that come with that interface: ```solidity function canTransfer(address, address, uint256) external view returns (bool); @@ -138,42 +100,21 @@ function created(address, uint256) external; function destroyed(address, uint256) external; ``` -A document engine has **nothing to do with token transfers**, so these would have to be stubbed as -no-ops (`canTransfer` always returning `true`). That is misleading: the contract would advertise a -transfer-compliance surface it does not honor, enlarging the ABI and inviting integrators to wire it -where a real compliance contract is expected. +A document engine has **nothing to do with token transfers**, so these would have to be stubbed as no-ops (`canTransfer` always returning `true`). That is misleading: the contract would advertise a transfer-compliance surface it does not honor, enlarging the ABI and inviting integrators to wire it where a real compliance contract is expected. -The binding concept we actually need is tiny — "is this caller a token allowed to manage its own -documents?" — so we implement just that: a **single allowlist** in `TokenBindingModule`, shared by -both deployments and gated by each one's document-management hook. It is deliberately **not** a -role: there is no `TOKEN_CONTRACT_ROLE`, and `DocumentEngineOwnable` uses the same allowlist rather -than a separate owner-managed one. This keeps the engine's surface honest and minimal while still -mirroring the RuleEngine binding pattern; the RuleEngine submodule is kept as a reference for that -pattern. +The binding concept we actually need is tiny — "is this caller a token allowed to manage its own documents?" — so we implement just that: a **single allowlist** in `TokenBindingModule`, shared by both deployments and gated by each one's document-management hook. It is deliberately **not** a role: there is no `TOKEN_CONTRACT_ROLE`, and `DocumentEngineOwnable` uses the same allowlist rather than a separate owner-managed one. This keeps the engine's surface honest and minimal while still mirroring the RuleEngine binding pattern; the RuleEngine submodule is kept as a reference for that pattern. ### Events -This engine is a **shared, multi-token** document manager, so — per the ERC-1643 -["Emission Responsibility"](./ERCSpecification/erc-1643.md) rules — it emits **only** the -address-carrying extension events `DocumentUpdatedForSubject(address indexed subject, …)` / -`DocumentRemovedForSubject(…)`, and **not** the base `DocumentUpdated` / `DocumentRemoved` events. -The base events carry no address and so cannot identify which token contract a change belongs to; -they are the responsibility of the token contract that exposes ERC-1643 to consumers (it re-emits -them when delegating). See the -[Multi-Subject Document Management draft](./ERCSpecification/erc-draft_multi_document_management.md) -and the `IERC1643MultiDocument` extension. +This engine is a **shared, multi-token** document manager, so — per the ERC-1643 ["Emission Responsibility"](./ERCSpecification/erc-1643.md) rules — it emits **only** the address-carrying extension events `DocumentUpdatedForSubject(address indexed subject, …)` / `DocumentRemovedForSubject(…)`, and **not** the base `DocumentUpdated` / `DocumentRemoved` events. The base events carry no address and so cannot identify which token contract a change belongs to; they are the responsibility of the token contract that exposes ERC-1643 to consumers (it re-emits them when delegating). See the [Multi-Subject Document Management draft](./ERCSpecification/erc-draft_multi_document_management.md) and the `IERC1643MultiDocument` extension. ### Integration with CMTAT -Since CMTAT v3, the shipped standalone tokens store documents on-chain (`DocumentERC1643Module`) and -do not consume an external engine through their constructor. To use this engine, a CMTAT token -relies on the `DocumentEngineModule` and is wired at runtime with `setDocumentEngine(engine)`; -reads/writes are then forwarded to the engine keyed by the token address. +Since CMTAT v3, the shipped standalone tokens store documents on-chain (`DocumentERC1643Module`) and do not consume an external engine through their constructor. To use this engine, a CMTAT token relies on the `DocumentEngineModule` and is wired at runtime with `setDocumentEngine(engine)`; reads/writes are then forwarded to the engine keyed by the token address. #### Topology -One engine serves a whole fleet of tokens. Each token keeps its own document namespace, keyed by its -address, and can never reach another token's: +One engine serves a whole fleet of tokens. Each token keeps its own document namespace, keyed by its address, and can never reach another token's: ![Topology: one engine, many subjects](./img/cmtat-integration-architecture.png) @@ -193,14 +134,9 @@ _Diagram source: `doc/img/cmtat-read-simple.puml`._ #### Wiring and the full call flow -Two independent steps wire a token to the engine, and they are easy to get half right: -`bindToken(token)` on the **engine** authorises the token to use the single-argument ERC-1643 -functions, while `setDocumentEngine(engine)` on the **token** tells it where to forward. Bind -without wiring and the token has nowhere to send; wire without binding and the forwarded call -reverts `NotBoundToken`. +Two independent steps wire a token to the engine, and they are easy to get half right: `bindToken(token)` on the **engine** authorises the token to use the single-argument ERC-1643 functions, while `setDocumentEngine(engine)` on the **token** tells it where to forward. Bind without wiring and the token has nowhere to send; wire without binding and the forwarded call reverts `NotBoundToken`. -The diagram below expands the two above with the wiring steps, every revert branch, and the admin -path — the one case where the emission split does not hold: +The diagram below expands the two above with the wiring steps, every revert branch, and the admin path — the one case where the emission split does not hold: ![DocumentEngine and CMTAT call sequence](./img/cmtat-integration-sequence.png) @@ -220,10 +156,7 @@ token.setDocumentEngine(documentEngine); token.setDocument(bytes32("prospectus"), "ipfs://...", keccak256(bytes(content))); ``` -Both halves are covered by the test suite against real CMTAT code: `testCanReturnCMTATDocument` -wires `CMTATDocumentEngineMock` (built on CMTAT's `DocumentEngineModule`) with `setDocumentEngine` -and reads through it, and `testBoundTokenCanManageOwnDocument` exercises the bound-token write and -the namespace isolation that goes with it. +Both halves are covered by the test suite against real CMTAT code: `testCanReturnCMTATDocument` wires `CMTATDocumentEngineMock` (built on CMTAT's `DocumentEngineModule`) with `setDocumentEngine` and reads through it, and `testBoundTokenCanManageOwnDocument` exercises the bound-token write and the namespace isolation that goes with it. @@ -235,33 +168,18 @@ The engine is split into two contracts (CMTAT module/deployment pattern): _Diagram source: `doc/img/documentengine-contract-structure.puml`._ -- **`DocumentEngineBase`** (abstract) — holds the document storage and all the ERC-1643 - document-management functions, plus the `onlyDocumentManager` / `onlyBoundToken` modifiers and the - **abstract** `_authorize*` hooks. It is agnostic to the access-control implementation. -- **`DocumentEngine`** (deployment) — the concrete, deployable contract. It defines the **access - control** (`AccessControlEnumerable`, the `_authorize*` hook implementations and the `hasRole` - override) and wires the ERC-2771 (gasless) support. `AccessControlEnumerable` additionally allows - enumerating the members of each role on-chain. -- **`DocumentEngineOwnable`** (alternative deployment) — same base logic, but access control is a - single **owner** via `Ownable2Step` (two-step ownership transfer) instead of roles. Both document - management and token binding are `owner`-only. -- **`TokenBindingModule`** (`src/modules/TokenBindingModule.sol`) — the shared token-binding - registry (an allowlist) implementing `ITokenBinding` (`bindToken` / `unbindToken` / `isTokenBound` + - `TokenBindingSet`). Both deployments inherit it, so binding is identical (same functions, event, - and `NotBoundToken` revert) and ERC-165-discoverable regardless of the access-control model; - binding is authorized by each deployment's document-management hook. - -`DocumentEngineInvariant` provides the errors shared by every deployment. Access-control specifics -are **not** defined there: the `DOCUMENT_MANAGER_ROLE` constant lives in the role-based -`DocumentEngine`, and the owner logic in `DocumentEngineOwnable`. - -`VersionModule` (`src/modules/VersionModule.sol`) isolates the version concern and implements -[ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) (see below). +- **`DocumentEngineBase`** (abstract) — holds the document storage and all the ERC-1643 document-management functions, plus the `onlyDocumentManager` / `onlyBoundToken` modifiers and the **abstract** `_authorize*` hooks. It is agnostic to the access-control implementation. +- **`DocumentEngine`** (deployment) — the concrete, deployable contract. It defines the **access control** (`AccessControlEnumerable`, the `_authorize*` hook implementations and the `hasRole` override) and wires the ERC-2771 (gasless) support. `AccessControlEnumerable` additionally allows enumerating the members of each role on-chain. +- **`DocumentEngineOwnable`** (alternative deployment) — same base logic, but access control is a single **owner** via `Ownable2Step` (two-step ownership transfer) instead of roles. Both document management and token binding are `owner`-only. +- **`TokenBindingModule`** (`src/modules/TokenBindingModule.sol`) — the shared token-binding registry (an allowlist) implementing `ITokenBinding` (`bindToken` / `unbindToken` / `isTokenBound` + `TokenBindingSet`). Both deployments inherit it, so binding is identical (same functions, event, and `NotBoundToken` revert) and ERC-165-discoverable regardless of the access-control model; binding is authorized by each deployment's document-management hook. + +`DocumentEngineInvariant` provides the errors shared by every deployment. Access-control specifics are **not** defined there: the `DOCUMENT_MANAGER_ROLE` constant lives in the role-based `DocumentEngine`, and the owner logic in `DocumentEngineOwnable`. + +`VersionModule` (`src/modules/VersionModule.sol`) isolates the version concern and implements [ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) (see below). ## Version (ERC-8303) -The contract version is exposed through the `VersionModule`, which implements the -[ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) `IERC8303` interface: +The contract version is exposed through the `VersionModule`, which implements the [ERC-8303](https://ethereum-magicians.org/t/erc-8303-contract-version/28795) `IERC8303` interface: ```solidity interface IERC8303 { @@ -269,11 +187,9 @@ interface IERC8303 { } ``` -- `version()` returns the current version string (e.g. `"0.4.0"`), following Semantic Versioning - 2.0.0. +- `version()` returns the current version string (e.g. `"0.4.0"`), following Semantic Versioning 2.0.0. - The public `VERSION` constant is kept for backward compatibility and returns the same value. -- ERC-165 discovery is supported: `supportsInterface(0x54fd4d50)` (the ERC-8303 interface id) - returns `true`. +- ERC-165 discovery is supported: `supportsInterface(0x54fd4d50)` (the ERC-8303 interface id) returns `true`. ### ERC-165: what the engine advertises @@ -288,30 +204,17 @@ Both deployments advertise: | `IERC165` | `0x01ffc9a7` | | | `IAccessControlEnumerable` | — | `DocumentEngine` only | -`type(IERC1643).interfaceId` is advertised because the engine really does implement the base -single-argument functions. Its audience is a **token wiring itself to the engine**: before calling -`setDocumentEngine(engine)`, or before forwarding `setDocument(name, uri, hash)`, a token can -confirm through ERC-165 that those endpoints exist here rather than discovering it from a failed -call. `ITokenBinding` answers the complementary question — does this engine have a binding surface — -and `isTokenBound(address(this))` whether that particular token may use it. +`type(IERC1643).interfaceId` is advertised because the engine really does implement the base single-argument functions. Its audience is a **token wiring itself to the engine**: before calling `setDocumentEngine(engine)`, or before forwarding `setDocument(name, uri, hash)`, a token can confirm through ERC-165 that those endpoints exist here rather than discovering it from a failed call. `ITokenBinding` answers the complementary question — does this engine have a binding surface — and `isTokenBound(address(this))` whether that particular token may use it. -> **It is not an invitation to read documents from this address.** The base functions are -> `_msgSender()`-scoped, so a third party calling `getDocument(name)` on the engine reads *its own*, -> empty namespace — no revert, no error, just nothing — and the engine emits only the -> address-carrying `*ForSubject` events. Point document consumers at the **subject**, or use the -> address-scoped `getDocument(subject, name)`. Asserted by -> `testBaseERC1643IsAdvertisedButReadsAreCallerScoped`. +> **It is not an invitation to read documents from this address.** The base functions are `_msgSender()`-scoped, so a third party calling `getDocument(name)` on the engine reads *its own*, empty namespace — no revert, no error, just nothing — and the engine emits only the address-carrying `*ForSubject` events. Point document consumers at the **subject**, or use the address-scoped `getDocument(subject, name)`. Asserted by `testBaseERC1643IsAdvertisedButReadsAreCallerScoped`. ## Schema -Generated with Surya — regenerate with the three scripts in [`doc/script`](./script). Diagrams for -**every** file in `src/`, interfaces included, live under [`doc/surya`](./surya); the ones below are -the two deployments and the base they share. +Generated with Surya — regenerate with the three scripts in [`doc/script`](./script). Diagrams for **every** file in `src/`, interfaces included, live under [`doc/surya`](./surya); the ones below are the two deployments and the base they share. ### Inheritance -Both deployments sit on the same two modules — `DocumentEngineBase` (document logic) and -`TokenBindingModule` (the binding allowlist) — and differ only in the access-control layer. +Both deployments sit on the same two modules — `DocumentEngineBase` (document logic) and `TokenBindingModule` (the binding allowlist) — and differ only in the access-control layer. #### `DocumentEngine` — role-based (`AccessControlEnumerable`) @@ -339,9 +242,7 @@ Both deployments sit on the same two modules — `DocumentEngineBase` (document ### Contracts Description Table -Per-file reports live in [`doc/surya/surya_report`](./surya/surya_report); the tables below merge -them. Note that the document functions belong to **`DocumentEngineBase`**, not to either deployment -— each deployment contributes only its access-control layer and its ERC-2771 context overrides. +Per-file reports live in [`doc/surya/surya_report`](./surya/surya_report); the tables below merge them. Note that the document functions belong to **`DocumentEngineBase`**, not to either deployment — each deployment contributes only its access-control layer and its ERC-2771 context overrides. | Contract | Type | Bases | | | |:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| @@ -430,43 +331,29 @@ them. Note that the document functions belong to **`DocumentEngineBase`**, not t ## Gasless support (ERC-2771) -The DocumentEngine supports client-side gasless transactions using the -[Gas Station Network](https://docs.opengsn.org/#the-problem) (GSN) pattern, the main open standard -for transfering fee payment to another account than that of the transaction issuer. The contract -uses the OpenZeppelin contract `ERC2771ContextUpgradeable`, which allows a contract to get the -original client with `_msgSender()` instead of the fee payer given by `msg.sender` while allowing -upgrades on the main contract (see *Deployment via a proxy* above). +The DocumentEngine supports client-side gasless transactions using the [Gas Station Network](https://docs.opengsn.org/#the-problem) (GSN) pattern, the main open standard for transfering fee payment to another account than that of the transaction issuer. The contract uses the OpenZeppelin contract `ERC2771ContextUpgradeable`, which allows a contract to get the original client with `_msgSender()` instead of the fee payer given by `msg.sender` while allowing upgrades on the main contract (see *Deployment via a proxy* above). -At deployment, the parameter `forwarder` inside the constructor has to be set with the defined -address of the forwarder. Please note that the forwarder can not be changed after deployment. +At deployment, the parameter `forwarder` inside the constructor has to be set with the defined address of the forwarder. Please note that the forwarder can not be changed after deployment. -Please see the OpenGSN [documentation](https://docs.opengsn.org/contracts/#receiving-a-relayed-call) -for more details on what is done to support GSN in the contract. +Please see the OpenGSN [documentation](https://docs.opengsn.org/contracts/#receiving-a-relayed-call) for more details on what is done to support GSN in the contract. ## Dependencies -The toolchain includes the following components, where the versions are the latest ones that we -tested: +The toolchain includes the following components, where the versions are the latest ones that we tested: - Foundry - Solidity 0.8.34 (via solc-js), `evm_version = prague` -- OpenZeppelin Contracts (submodule) - [v5.7.0](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.7.0) +- OpenZeppelin Contracts (submodule) [v5.7.0](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.7.0) - Tests - [CMTAT v3.3.0-rc3](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) - - [RuleEngine v3.0.0-rc5](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) - (binding-pattern reference only — its compliance module is - [not reused](#why-not-reuse-ruleengines-erc-3643-compliance-module)) - - OpenZeppelin Contracts Upgradeable (submodule) - [v5.7.0](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v5.7.0) + - [RuleEngine v3.0.0-rc5](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) (binding-pattern reference only — its compliance module is [not reused](#why-not-reuse-ruleengines-erc-3643-compliance-module)) + - OpenZeppelin Contracts Upgradeable (submodule) [v5.7.0](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v5.7.0) ### Version compatibility -Each release of this engine is built and tested against one CMTAT release. CMTAT's `IERC1643` is not -stable across its own release candidates, so pairing a version of this engine with a different CMTAT -than the one below is not supported. +Each release of this engine is built and tested against one CMTAT release. CMTAT's `IERC1643` is not stable across its own release candidates, so pairing a version of this engine with a different CMTAT than the one below is not supported. | DocumentEngine | CMTAT | Solidity / `evm_version` | OpenZeppelin | `getDocument` returns | | -------------- | ----- | ------------------------ | ------------ | --------------------- | @@ -477,23 +364,13 @@ than the one below is not supported. Notes on the CMTAT v2 → v3 jump at `v0.4.0`: -- **CMTAT `v3.3.0-rc1` is not supported.** It is the one release in which `IERC1643.getDocument` - returns a `Document` struct rather than the three flat values; `v3.3.0-rc2` reverted that and - `v3.3.0-rc3` keeps the flat return. rc1 also does not declare `ERC1643InvalidName` / - `ERC1643MissingDocument` on the interface. Building this engine against rc1 fails to compile. -- **`v3.3.0-rc2` → `v3.3.0-rc3` is a no-op for this engine.** The only change to the document - surface (`draft-IERC1643.sol`, `IDocumentEngine.sol`, `DocumentEngineModule.sol`, - `DocumentERC1643Module.sol`) is a pragma bump from `^0.8.20` to `^0.8.24`; the interface, the - errors and the `getDocument` return shape are unchanged. -- The `IERC1643` import path moved in CMTAT v3, from `CMTAT/interfaces/engine/draft-IERC1643.sol` to - `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. +- **CMTAT `v3.3.0-rc1` is not supported.** It is the one release in which `IERC1643.getDocument` returns a `Document` struct rather than the three flat values; `v3.3.0-rc2` reverted that and `v3.3.0-rc3` keeps the flat return. rc1 also does not declare `ERC1643InvalidName` / `ERC1643MissingDocument` on the interface. Building this engine against rc1 fails to compile. +- **`v3.3.0-rc2` → `v3.3.0-rc3` is a no-op for this engine.** The only change to the document surface (`draft-IERC1643.sol`, `IDocumentEngine.sol`, `DocumentEngineModule.sol`, `DocumentERC1643Module.sol`) is a pragma bump from `^0.8.20` to `^0.8.24`; the interface, the errors and the `getDocument` return shape are unchanged. +- The `IERC1643` import path moved in CMTAT v3, from `CMTAT/interfaces/engine/draft-IERC1643.sol` to `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. - Document names became `bytes32` in CMTAT v3 (they were `string` up to v2.5.0-rc0). - Two different Solidity floors apply from `v0.4.0` on, and the sources declare the lower of them: - - **`src/` requires `≥ 0.8.24`** — the pragma every file declares. OpenZeppelin's - `AccessControlEnumerable.sol` / `EnumerableSet.sol` and, since CMTAT `v3.3.0-rc3`, - `draft-IERC1643.sol` are all `^0.8.24`, so no contract here compiles below it. - - **Building the full project, tests included, requires `≥ 0.8.27`**, because CMTAT v3 uses - `require(cond, CustomError())`, which is restricted to the via-ir pipeline before `0.8.27`. + - **`src/` requires `≥ 0.8.24`** — the pragma every file declares. OpenZeppelin's `AccessControlEnumerable.sol` / `EnumerableSet.sol` and, since CMTAT `v3.3.0-rc3`, `draft-IERC1643.sol` are all `^0.8.24`, so no contract here compiles below it. + - **Building the full project, tests included, requires `≥ 0.8.27`**, because CMTAT v3 uses `require(cond, CustomError())`, which is restricted to the via-ir pipeline before `0.8.27`. This is why the declared pragma is `^0.8.24` while `foundry.toml` pins `0.8.34`. @@ -503,8 +380,7 @@ Exact submodule revisions are pinned in [`foundry.lock`](../foundry.lock). ### Formatting (forge fmt) -`forge fmt` is the canonical formatter for this project (configured under `[fmt]` in -`foundry.toml`): +`forge fmt` is the canonical formatter for this project (configured under `[fmt]` in `foundry.toml`): ```bash forge fmt # format src/, test/, script/ @@ -513,10 +389,7 @@ forge fmt --check # verify formatting (CI) ### Static analysis -Reports are versioned under [`doc/audits/tools/`](./audits/tools), one directory per release, each -with the raw tool output (prefixed by a summary table) and a feedback file triaging every finding -against the source. The security overview is -[`doc/audits/AUDIT_OVERVIEW.md`](./audits/AUDIT_OVERVIEW.md). +Reports are versioned under [`doc/audits/tools/`](./audits/tools), one directory per release, each with the raw tool output (prefixed by a summary table) and a feedback file triaging every finding against the source. The security overview is [`doc/audits/AUDIT_OVERVIEW.md`](./audits/AUDIT_OVERVIEW.md). | Release | Tool | Result | Report | Triage | | ------- | ---- | ------ | ------ | ------ | @@ -533,23 +406,13 @@ slither . --checklist --filter-paths "node_modules,lib,test,forge-std,mocks" \ > doc/audits/tools/v0.4.0/slither/slither-report.md ``` -> **Filter on `lib`, not on individual submodule names.** This is a Foundry project, so every -> dependency lives under `lib/`. `--filter-paths` fails *open* — an entry matching nothing silently -> widens scope instead of erroring — so naming submodules one by one risks pulling a whole vendored -> tree into the report. Verify with `grep -c 'lib/\|node_modules/' `, which must return `0`. -> Slither also writes its checklist to **stdout** and its detector log to **stderr**, and exits -> non-zero when it finds anything: `exit=255` with a populated report is the normal outcome. +> **Filter on `lib`, not on individual submodule names.** This is a Foundry project, so every dependency lives under `lib/`. `--filter-paths` fails *open* — an entry matching nothing silently widens scope instead of erroring — so naming submodules one by one risks pulling a whole vendored tree into the report. Verify with `grep -c 'lib/\|node_modules/' `, which must return `0`. Slither also writes its checklist to **stdout** and its detector log to **stderr**, and exits non-zero when it finds anything: `exit=255` with a populated report is the normal outcome. -> **Static-analysis output is leads, not findings.** Every dismissal in the feedback files was -> verified against the cited `file:line`, and neither tool can see the specification-level issues -> that matter most here — those are tracked under *Known open items* in -> [`AUDIT_OVERVIEW.md`](./audits/AUDIT_OVERVIEW.md). +> **Static-analysis output is leads, not findings.** Every dismissal in the feedback files was verified against the cited `file:line`, and neither tool can see the specification-level issues that matter most here — those are tracked under *Known open items* in [`AUDIT_OVERVIEW.md`](./audits/AUDIT_OVERVIEW.md). ### Surya -Three scripts in [`doc/script`](./script) regenerate the diagrams and reports for every `.sol` under -`src/`, writing into a scratch `docOut/` at the repo root. **Run them from `doc/script/` and in this -order** — the graph script creates `docOut/`, and the report script's `mkdir` has no `-p`: +Three scripts in [`doc/script`](./script) regenerate the diagrams and reports for every `.sol` under `src/`, writing into a scratch `docOut/` at the repo root. **Run them from `doc/script/` and in this order** — the graph script creates `docOut/`, and the report script's `mkdir` has no `-p`: ```bash (cd doc/script && bash script_surya_graph.sh) @@ -557,29 +420,18 @@ order** — the graph script creates `docOut/`, and the report script's `mkdir` (cd doc/script && bash script_surya_report.sh) ``` -Then replace the three directories under [`doc/surya`](./surya) with the fresh output. Requires -Graphviz (`dot`) — the graph and inheritance scripts pipe through it. +Then replace the three directories under [`doc/surya`](./surya) with the fresh output. Requires Graphviz (`dot`) — the graph and inheritance scripts pipe through it. -> **Known Surya bug — check for 0-byte PNGs.** `surya graph` parses only the file it is given, so a -> `super.()` call into a base declared elsewhere throws -> `TypeError: Cannot read properties of undefined (reading 'includes')`. Piped into `dot`, that -> surfaces as a silent **empty PNG**, not an error. Four files here call `super.()` -> (`DocumentEngine`, `DocumentEngineOwnable`, `VersionModule`, `TokenBindingModule`), so the guard -> in `surya/lib/graph.js` — -> `functionsPerContract[contract] && functionsPerContract[contract].includes(name)` — must be -> applied before regenerating. It lives in `node_modules` (or the `npx` cache) and is reverted by -> any reinstall. +> **Known Surya bug — check for 0-byte PNGs.** `surya graph` parses only the file it is given, so a `super.()` call into a base declared elsewhere throws `TypeError: Cannot read properties of undefined (reading 'includes')`. Piped into `dot`, that surfaces as a silent **empty PNG**, not an error. Four files here call `super.()` (`DocumentEngine`, `DocumentEngineOwnable`, `VersionModule`, `TokenBindingModule`), so the guard in `surya/lib/graph.js` — `functionsPerContract[contract] && functionsPerContract[contract].includes(name)` — must be applied before regenerating. It lives in `node_modules` (or the `npx` cache) and is reverted by any reinstall. ### Foundry -Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written -in Rust. +Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust. Foundry consists of: - **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). -- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and - getting chain data. +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. - **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. - **Chisel**: Fast, utilitarian, and verbose solidity REPL. @@ -633,8 +485,7 @@ $ anvil ##### Deploy -Two deployment scripts are provided in [`script/`](../script), one per access-control variant. Both -read their configuration from environment variables: +Two deployment scripts are provided in [`script/`](../script), one per access-control variant. Both read their configuration from environment variables: | Variable | Used by | Default | Meaning | | --- | --- | --- | --- | @@ -642,13 +493,7 @@ read their configuration from environment variables: | `DOCUMENT_ENGINE_OWNER` | `DeployDocumentEngineOwnable` | `msg.sender` | initial owner | | `DOCUMENT_ENGINE_FORWARDER` | both | `address(0)` | ERC-2771 trusted forwarder (`address(0)` disables gasless) | -> **Warning** ->These environment variables, and passing a raw key with `--private-key`, are intended for **local ->testing only — do not use them in production**. A private key supplied on the command line or ->through an environment variable is exposed in your shell history and process environment. For ->production deployments, use a secure signing method (encrypted keystore, hardware wallet, ...) as ->described in the Foundry Key Management documentation (getfoundry.sh) for securely broadcasting ->transactions through a script. +> **Warning** These environment variables, and passing a raw key with `--private-key`, are intended for **local testing only — do not use them in production**. A private key supplied on the command line or through an environment variable is exposed in your shell history and process environment. For production deployments, use a secure signing method (encrypted keystore, hardware wallet, ...) as described in the Foundry Key Management documentation (getfoundry.sh) for securely broadcasting transactions through a script. ```shell # Role-based DocumentEngine (AccessControlEnumerable) @@ -662,8 +507,7 @@ $ DOCUMENT_ENGINE_OWNER=0xYourOwner \ --rpc-url --private-key --broadcast ``` -Drop `--broadcast` (and `--rpc-url`) for a local dry-run. The scripts are covered by -[`test/Deploy.t.sol`](../test/Deploy.t.sol). +Drop `--broadcast` (and `--rpc-url`) for a local dry-run. The scripts are covered by [`test/Deploy.t.sol`](../test/Deploy.t.sol). ##### Cast @@ -681,5 +525,4 @@ $ cast --help ## Intellectual property -The code is copyright (c) Capital Market and Technology Association, 2018-2024, and is released -under [Mozilla Public License 2.0](https://github.com/CMTA/CMTAT/blob/master/LICENSE.md). +The code is copyright (c) Capital Market and Technology Association, 2018-2024, and is released under [Mozilla Public License 2.0](https://github.com/CMTA/CMTAT/blob/master/LICENSE.md). From 899ff3fd699ca2d8e3c7ad4bc95719849a978b24 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 15:08:40 +0200 Subject: [PATCH 44/47] Run npm audit fix --- package-lock.json | 3808 +++++++++++++++++---------------------------- 1 file changed, 1446 insertions(+), 2362 deletions(-) diff --git a/package-lock.json b/package-lock.json index c44ede8..13a3795 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,12 +4,114 @@ "requires": true, "packages": { "": { + "name": "DocumentEngine", "devDependencies": { - "prettier-plugin-solidity": "^1.4.1", "solidity-docgen": "^0.6.0-beta.36", "surya": "^0.4.11" } }, + "node_modules/@ethereumjs/rlp": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.2.tgz", + "integrity": "sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "bin": { + "rlp": "bin/rlp.cjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ethereumjs/util": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-9.1.0.tgz", + "integrity": "sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "dependencies": { + "@ethereumjs/rlp": "^5.0.2", + "ethereum-cryptography": "^2.2.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ethereumjs/util/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, "node_modules/@ethersproject/abi": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", @@ -155,9 +257,9 @@ } }, "node_modules/@ethersproject/bytes": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", - "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.8.0.tgz", + "integrity": "sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A==", "dev": true, "funding": [ { @@ -169,9 +271,10 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], + "license": "MIT", "peer": true, "dependencies": { - "@ethersproject/logger": "^5.7.0" + "@ethersproject/logger": "^5.8.0" } }, "node_modules/@ethersproject/constants": { @@ -244,9 +347,9 @@ } }, "node_modules/@ethersproject/logger": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", - "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.8.0.tgz", + "integrity": "sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA==", "dev": true, "funding": [ { @@ -258,6 +361,7 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], + "license": "MIT", "peer": true }, "node_modules/@ethersproject/networks": { @@ -281,9 +385,9 @@ } }, "node_modules/@ethersproject/properties": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", - "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.8.0.tgz", + "integrity": "sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==", "dev": true, "funding": [ { @@ -295,9 +399,10 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], + "license": "MIT", "peer": true, "dependencies": { - "@ethersproject/logger": "^5.7.0" + "@ethersproject/logger": "^5.8.0" } }, "node_modules/@ethersproject/rlp": { @@ -322,9 +427,9 @@ } }, "node_modules/@ethersproject/signing-key": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", - "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.8.0.tgz", + "integrity": "sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==", "dev": true, "funding": [ { @@ -336,13 +441,14 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], + "license": "MIT", "peer": true, "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", "bn.js": "^5.2.1", - "elliptic": "6.5.4", + "elliptic": "6.6.1", "hash.js": "1.1.7" } }, @@ -430,21 +536,145 @@ "node": ">=14" } }, - "node_modules/@metamask/eth-sig-util": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", - "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, + "license": "ISC", "peer": true, "dependencies": { - "ethereumjs-abi": "^0.6.8", - "ethereumjs-util": "^6.2.1", - "ethjs-util": "^0.1.6", - "tweetnacl": "^1.0.3", - "tweetnacl-util": "^0.15.1" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@noble/curves": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.2.tgz", + "integrity": "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.7.2" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz", + "integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/@noble/hashes": { @@ -474,209 +704,100 @@ "peer": true }, "node_modules/@nomicfoundation/edr": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.5.2.tgz", - "integrity": "sha512-hW/iLvUQZNTVjFyX/I40rtKvvDOqUEyIi96T28YaLfmPL+3LW2lxmYLUXEJ6MI14HzqxDqrLyhf6IbjAa2r3Dw==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.23.tgz", + "integrity": "sha512-F2/6HZh8Q9RsgkOIkRrckldbhPjIZY7d4mT9LYuW68miwGQ5l7CkAgcz9fRRiurA0+YJhtsbx/EyrD9DmX9BOw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@nomicfoundation/edr-darwin-arm64": "0.5.2", - "@nomicfoundation/edr-darwin-x64": "0.5.2", - "@nomicfoundation/edr-linux-arm64-gnu": "0.5.2", - "@nomicfoundation/edr-linux-arm64-musl": "0.5.2", - "@nomicfoundation/edr-linux-x64-gnu": "0.5.2", - "@nomicfoundation/edr-linux-x64-musl": "0.5.2", - "@nomicfoundation/edr-win32-x64-msvc": "0.5.2" + "@nomicfoundation/edr-darwin-arm64": "0.12.0-next.23", + "@nomicfoundation/edr-darwin-x64": "0.12.0-next.23", + "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.23", + "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.23", + "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.23", + "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.23", + "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.23" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@nomicfoundation/edr-darwin-arm64": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.5.2.tgz", - "integrity": "sha512-Gm4wOPKhbDjGTIRyFA2QUAPfCXA1AHxYOKt3yLSGJkQkdy9a5WW+qtqKeEKHc/+4wpJSLtsGQfpzyIzggFfo/A==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.23.tgz", + "integrity": "sha512-Amh7mRoDzZyJJ4efqoePqdoZOzharmSOttZuJDlVE5yy07BoE8hL6ZRpa5fNYn0LCqn/KoWs8OHANWxhKDGhvQ==", "dev": true, + "license": "MIT", "peer": true, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@nomicfoundation/edr-darwin-x64": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.5.2.tgz", - "integrity": "sha512-ClyABq2dFCsrYEED3/UIO0c7p4H1/4vvlswFlqUyBpOkJccr75qIYvahOSJRM62WgUFRhbSS0OJXFRwc/PwmVg==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.23.tgz", + "integrity": "sha512-9wn489FIQm7m0UCD+HhktjWx6vskZzeZD9oDc2k9ZvbBzdXwPp5tiDqUBJ+eQpByAzCDfteAJwRn2lQCE0U+Iw==", "dev": true, + "license": "MIT", "peer": true, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.5.2.tgz", - "integrity": "sha512-HWMTVk1iOabfvU2RvrKLDgtFjJZTC42CpHiw2h6rfpsgRqMahvIlx2jdjWYzFNy1jZKPTN1AStQ/91MRrg5KnA==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.23.tgz", + "integrity": "sha512-nlk5EejSzEUfEngv0Jkhqq3/wINIfF2ED9wAofc22w/V1DV99ASh9l3/e/MIHOQFecIZ9MDqt0Em9/oDyB1Uew==", "dev": true, + "license": "MIT", "peer": true, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@nomicfoundation/edr-linux-arm64-musl": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.5.2.tgz", - "integrity": "sha512-CwsQ10xFx/QAD5y3/g5alm9+jFVuhc7uYMhrZAu9UVF+KtVjeCvafj0PaVsZ8qyijjqVuVsJ8hD1x5ob7SMcGg==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.23.tgz", + "integrity": "sha512-SJuPBp3Rc6vM92UtVTUxZQ/QlLhLfwTftt2XUiYohmGKB3RjGzpgduEFMCA0LEnucUckU6UHrJNFHiDm77C4PQ==", "dev": true, + "license": "MIT", "peer": true, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@nomicfoundation/edr-linux-x64-gnu": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.5.2.tgz", - "integrity": "sha512-CWVCEdhWJ3fmUpzWHCRnC0/VLBDbqtqTGTR6yyY1Ep3S3BOrHEAvt7h5gx85r2vLcztisu2vlDq51auie4IU1A==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.23.tgz", + "integrity": "sha512-NU+Qs3u7Qt6t3bJFdmmjd5CsvgI2bPPzO31KifM2Ez96/jsXYho5debtTQnimlb5NAqiHTSlxjh/F8ROcptmeQ==", "dev": true, + "license": "MIT", "peer": true, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@nomicfoundation/edr-linux-x64-musl": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.5.2.tgz", - "integrity": "sha512-+aJDfwhkddy2pP5u1ISg3IZVAm0dO836tRlDTFWtvvSMQ5hRGqPcWwlsbobhDQsIxhPJyT7phL0orCg5W3WMeA==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.23.tgz", + "integrity": "sha512-F78fZA2h6/ssiCSZOovlgIu0dUeI7ItKPsDDF3UUlIibef052GCXmliMinC90jVPbrjUADMd1BUwjfI0Z8OllQ==", "dev": true, + "license": "MIT", "peer": true, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@nomicfoundation/edr-win32-x64-msvc": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.5.2.tgz", - "integrity": "sha512-CcvvuA3sAv7liFNPsIR/68YlH6rrybKzYttLlMr80d4GKJjwJ5OKb3YgE6FdZZnOfP19HEHhsLcE0DPLtY3r0w==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@nomicfoundation/ethereumjs-common": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz", - "integrity": "sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg==", - "dev": true, - "peer": true, - "dependencies": { - "@nomicfoundation/ethereumjs-util": "9.0.4" - } - }, - "node_modules/@nomicfoundation/ethereumjs-rlp": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz", - "integrity": "sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw==", - "dev": true, - "peer": true, - "bin": { - "rlp": "bin/rlp.cjs" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@nomicfoundation/ethereumjs-tx": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz", - "integrity": "sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw==", - "dev": true, - "peer": true, - "dependencies": { - "@nomicfoundation/ethereumjs-common": "4.0.4", - "@nomicfoundation/ethereumjs-rlp": "5.0.4", - "@nomicfoundation/ethereumjs-util": "9.0.4", - "ethereum-cryptography": "0.1.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "c-kzg": "^2.1.2" - }, - "peerDependenciesMeta": { - "c-kzg": { - "optional": true - } - } - }, - "node_modules/@nomicfoundation/ethereumjs-tx/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "peer": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/@nomicfoundation/ethereumjs-util": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz", - "integrity": "sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.23.tgz", + "integrity": "sha512-IfJZQJn7d/YyqhmguBIGoCKjE9dKjbu6V6iNEPApfwf5JyyjHYyyfkLU4rf7hygj57bfH4sl1jtQ6r8HnT62lw==", "dev": true, + "license": "MIT", "peer": true, - "dependencies": { - "@nomicfoundation/ethereumjs-rlp": "5.0.4", - "ethereum-cryptography": "0.1.3" - }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "c-kzg": "^2.1.2" - }, - "peerDependenciesMeta": { - "c-kzg": { - "optional": true - } - } - }, - "node_modules/@nomicfoundation/ethereumjs-util/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "peer": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" + "node": ">= 20" } }, "node_modules/@nomicfoundation/solidity-analyzer": { @@ -775,6 +896,18 @@ "node": ">= 12" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@scure/base": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.7.tgz", @@ -929,59 +1062,6 @@ "node": ">=6" } }, - "node_modules/@solidity-parser/parser": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.18.0.tgz", - "integrity": "sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA==", - "dev": true - }, - "node_modules/@types/bn.js": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", - "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", - "dev": true, - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", - "dev": true, - "peer": true - }, - "node_modules/@types/node": { - "version": "22.5.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.1.tgz", - "integrity": "sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==", - "dev": true, - "peer": true, - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/@types/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", - "dev": true, - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/secp256k1": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz", - "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==", - "dev": true, - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/adm-zip": { "version": "0.4.16", "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", @@ -1098,25 +1178,12 @@ "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", "dev": true }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "peer": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, + "license": "Python-2.0", "peer": true }, "node_modules/array-buffer-byte-length": { @@ -1197,43 +1264,15 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, - "peer": true - }, - "node_modules/base-x": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.10.tgz", - "integrity": "sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==", - "dev": true, - "peer": true, - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/blakejs": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", - "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==", - "dev": true, + "license": "MIT", "peer": true }, "node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.5.tgz", + "integrity": "sha512-Vq886eXykuP5E6HcKSSStP3bJgrE6In5WKxVUvJ8XGpWWYs2xZHWqUwzCtGgEtBcxyd57KBFDPFoUfNzdaHCNg==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/boxen": { @@ -1259,46 +1298,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/boxen/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/boxen/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/boxen/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -1313,27 +1312,14 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" + "balanced-match": "^1.0.0" } }, "node_modules/brorand": { @@ -1348,45 +1334,9 @@ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true, + "license": "ISC", "peer": true }, - "node_modules/browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "peer": true, - "dependencies": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", - "dev": true, - "peer": true, - "dependencies": { - "base-x": "^3.0.2" - } - }, - "node_modules/bs58check": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", - "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", - "dev": true, - "peer": true, - "dependencies": { - "bs58": "^4.0.0", - "create-hash": "^1.1.0", - "safe-buffer": "^5.1.2" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -1394,13 +1344,6 @@ "dev": true, "peer": true }, - "node_modules/buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true, - "peer": true - }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1450,73 +1393,52 @@ } }, "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "color-convert": "^1.9.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "peer": true, - "dependencies": { - "color-name": "1.1.3" + "node": ">=8" } }, - "node_modules/chalk/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "peer": true - }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" } }, "node_modules/ci-info": { @@ -1526,17 +1448,6 @@ "dev": true, "peer": true }, - "node_modules/cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "peer": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -1618,13 +1529,6 @@ "node": ">= 12" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "peer": true - }, "node_modules/cookie": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", @@ -1635,33 +1539,20 @@ "node": ">= 0.6" } }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "peer": true, - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, "node_modules/data-view-buffer": { @@ -1738,6 +1629,7 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=10" @@ -1791,20 +1683,30 @@ } }, "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, + "license": "BSD-3-Clause", "peer": true, "engines": { "node": ">=0.3.1" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "bn.js": "^4.11.9", @@ -1817,10 +1719,11 @@ } }, "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "version": "4.12.5", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.5.tgz", + "integrity": "sha512-3aRg6/JxfffFD+OlOjOFR3Vo79l39ooBTFucxx+MT3dhCtzn3EmiUPQo+6/OZuI2jbXi3YKgmiTFBgChQMwIRQ==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/emoji-regex": { @@ -1996,13 +1899,17 @@ } }, "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "peer": true, "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ethereum-cryptography": { @@ -2018,131 +1925,41 @@ "@scure/bip39": "1.1.1" } }, - "node_modules/ethereumjs-abi": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", - "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", - "dev": true, - "peer": true, - "dependencies": { - "bn.js": "^4.11.8", - "ethereumjs-util": "^6.0.0" - } - }, - "node_modules/ethereumjs-abi/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true - }, - "node_modules/ethereumjs-util": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", - "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", - "dev": true, - "peer": true, - "dependencies": { - "@types/bn.js": "^4.11.3", - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "0.1.6", - "rlp": "^2.2.3" - } - }, - "node_modules/ethereumjs-util/node_modules/@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", - "dev": true, - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/ethereumjs-util/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true - }, - "node_modules/ethereumjs-util/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "peer": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/ethjs-util": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", - "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, + "license": "MIT", "peer": true, - "dependencies": { - "is-hex-prefixed": "1.0.0", - "strip-hex-prefix": "1.0.0" - }, "engines": { - "node": ">=6.5.0", - "npm": ">=3" - } - }, - "node_modules/evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "peer": true, - "dependencies": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "peer": true, - "dependencies": { - "to-regex-range": "^5.0.1" + "node": ">=12.0.0" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "locate-path": "^2.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat": { @@ -2150,15 +1967,16 @@ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, + "license": "BSD-3-Clause", "peer": true, "bin": { "flat": "cli.js" } }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "dev": true, "funding": [ { @@ -2166,6 +1984,7 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "peer": true, "engines": { "node": ">=4.0" @@ -2185,6 +2004,24 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fp-ts": { "version": "1.19.3", "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", @@ -2207,28 +2044,6 @@ "node": ">=6 <7 || >=8" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "peer": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -2311,40 +2126,28 @@ } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, + "license": "ISC", "peer": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": "*" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "peer": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", @@ -2393,10 +2196,11 @@ } }, "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "dev": true, + "license": "MIT", "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", @@ -2414,51 +2218,48 @@ } }, "node_modules/hardhat": { - "version": "2.22.9", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.9.tgz", - "integrity": "sha512-sWiuI/yRdFUPfndIvL+2H18Vs2Gav0XacCFYY5msT5dHOWkhLxESJySIk9j83mXL31aXL8+UMA9OgViFLexklg==", + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.29.0.tgz", + "integrity": "sha512-tsj5mCSjDCFOhGfBl4vwqDEcwdlES9VUzRWfdrwvEVhus6D8W6u+WfUKRLLwFhKGS/8lKPoXGsjYWPXl3CCpOg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { + "@ethereumjs/util": "^9.1.0", "@ethersproject/abi": "^5.1.2", - "@metamask/eth-sig-util": "^4.0.0", - "@nomicfoundation/edr": "^0.5.2", - "@nomicfoundation/ethereumjs-common": "4.0.4", - "@nomicfoundation/ethereumjs-tx": "5.0.4", - "@nomicfoundation/ethereumjs-util": "9.0.4", + "@nomicfoundation/edr": "0.12.0-next.23", "@nomicfoundation/solidity-analyzer": "^0.1.0", "@sentry/node": "^5.18.1", - "@types/bn.js": "^5.1.0", - "@types/lru-cache": "^5.1.0", "adm-zip": "^0.4.16", "aggregate-error": "^3.0.0", "ansi-escapes": "^4.3.0", "boxen": "^5.1.2", - "chalk": "^2.4.2", - "chokidar": "^3.4.0", + "chokidar": "^4.0.0", "ci-info": "^2.0.0", "debug": "^4.1.1", "enquirer": "^2.3.0", "env-paths": "^2.2.0", "ethereum-cryptography": "^1.0.3", - "ethereumjs-abi": "^0.6.8", - "find-up": "^2.1.0", + "find-up": "^5.0.0", "fp-ts": "1.19.3", "fs-extra": "^7.0.1", - "glob": "7.2.0", "immutable": "^4.0.0-rc.12", "io-ts": "1.10.4", + "json-stream-stringify": "^3.1.4", "keccak": "^3.0.2", "lodash": "^4.17.11", + "micro-eth-signer": "^0.14.0", "mnemonist": "^0.38.0", - "mocha": "^10.0.0", + "mocha": "^11.1.0", "p-map": "^4.0.0", + "picocolors": "^1.1.0", "raw-body": "^2.4.1", "resolve": "1.17.0", "semver": "^6.3.0", "solc": "0.8.26", "source-map-support": "^0.5.13", "stacktrace-parser": "^0.1.10", + "tinyglobby": "^0.2.6", "tsort": "0.0.1", "undici": "^5.14.0", "uuid": "^8.3.2", @@ -2500,13 +2301,14 @@ } }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "peer": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/has-property-descriptors": { @@ -2560,21 +2362,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, - "peer": true, - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/hash.js": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", @@ -2619,6 +2406,7 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, + "license": "MIT", "peer": true, "bin": { "he": "bin/he" @@ -2681,10 +2469,11 @@ } }, "node_modules/immutable": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", - "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.9.tgz", + "integrity": "sha512-ObHy4YN7ycwZOUCLI1/6svfyAFu7vL8RhAvVu/bh/RZW9EPlOyDaQ9jDQWCtdqzaXUjgXZCW1migtHE7YI7UGQ==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/indent-string": { @@ -2697,18 +2486,6 @@ "node": ">=8" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "peer": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -2768,19 +2545,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "peer": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -2839,16 +2603,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -2858,30 +2612,6 @@ "node": ">=8" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "peer": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hex-prefixed": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", - "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.5.0", - "npm": ">=3" - } - }, "node_modules/is-negative-zero": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", @@ -2894,16 +2624,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/is-number-object": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", @@ -2919,11 +2639,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=8" @@ -3022,6 +2754,7 @@ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=10" @@ -3048,6 +2781,31 @@ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "peer": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", @@ -3056,10 +2814,21 @@ "peer": true }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", "peer": true, "dependencies": { "argparse": "^2.0.1" @@ -3068,6 +2837,17 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-stream-stringify": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.1.7.tgz", + "integrity": "sha512-F4MWetLtY42YMaAKw5cV4e47zMD5aOT+tjjQWjX18ACtdkQ5Y/vrcfbcQ107Rh+MXjOCIx4KhW0wPmOvG8iQ5w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=7.10.1" + } + }, "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -3084,6 +2864,7 @@ "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", "dev": true, "hasInstallScript": true, + "license": "MIT", "peer": true, "dependencies": { "node-addon-api": "^2.0.0", @@ -3095,24 +2876,28 @@ } }, "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/log-symbols": { @@ -3120,6 +2905,7 @@ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "chalk": "^4.1.0", @@ -3132,73 +2918,81 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } + "peer": true }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", "dev": true, "peer": true, "engines": { - "node": ">=8" + "node": ">= 0.10.0" } }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/micro-eth-signer": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/micro-eth-signer/-/micro-eth-signer-0.14.0.tgz", + "integrity": "sha512-5PLLzHiVYPWClEvZIXXFu5yutzpadb73rnQCpUqIHu3No3coFuWQNfE5tkBQJ7djuLYl6aRLaS0MgWJYGoqiBw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "@noble/curves": "~1.8.1", + "@noble/hashes": "~1.7.1", + "micro-packed": "~0.7.2" } }, - "node_modules/lru_map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", - "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", + "node_modules/micro-eth-signer/node_modules/@noble/hashes": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz", + "integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==", "dev": true, - "peer": true + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "node_modules/micro-packed": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/micro-packed/-/micro-packed-0.7.3.tgz", + "integrity": "sha512-2Milxs+WNC00TRlem41oRswvw31146GiSaoCT7s3Xi2gMUglW5QBeqlQaZeHr5tJx9nm3i57LNXPqxOOaWtTYg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "node_modules/micro-packed/node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", "dev": true, + "license": "MIT", "peer": true, - "engines": { - "node": ">= 0.10.0" + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/minimalistic-assert": { @@ -3216,16 +3010,20 @@ "peer": true }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, + "license": "ISC", "peer": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.2" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -3237,6 +3035,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "peer": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mnemonist": { "version": "0.38.5", "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", @@ -3248,31 +3057,33 @@ } }, "node_modules/mocha": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", - "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.8.0.tgz", + "integrity": "sha512-VyCeUdGN3A9lmCTTgG4yuvY9ixxaDk+xt2R/7/+1AP6EqNG+G9OKkzBwhVtVYoNX8YsxNSgAl8mOv3IAeOpFbw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "ansi-colors": "^4.1.3", "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", + "chokidar": "^4.0.1", "debug": "^4.3.5", - "diff": "^5.2.0", + "diff": "^7.0.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", - "glob": "^8.1.0", + "glob": "^10.4.5", "he": "^1.2.0", + "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", + "minimatch": "^9.0.5", "ms": "^2.1.3", + "picocolors": "^1.1.1", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" }, "bin": { @@ -3280,119 +3091,7 @@ "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/mocha/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "peer": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "peer": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "peer": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "peer": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/mocha/node_modules/ms": { @@ -3400,95 +3099,9 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, + "license": "MIT", "peer": true }, - "node_modules/mocha/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "peer": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "peer": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "peer": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "peer": true, - "engines": { - "node": ">=10" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -3507,13 +3120,15 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/node-gyp-build": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", - "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", "dev": true, + "license": "MIT", "peer": true, "bin": { "node-gyp-build": "bin.js", @@ -3521,16 +3136,6 @@ "node-gyp-build-test": "build-test.js" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-inspect": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", @@ -3577,16 +3182,6 @@ "dev": true, "peer": true }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "peer": true, - "dependencies": { - "wrappy": "1" - } - }, "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -3598,29 +3193,37 @@ } }, "node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "p-try": "^1.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "p-limit": "^1.1.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-map": { @@ -3639,34 +3242,34 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } + "license": "BlueOak-1.0.0", + "peer": true }, "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "peer": true, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "peer": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/path-parse": { @@ -3676,31 +3279,41 @@ "dev": true, "peer": true }, - "node_modules/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, + "license": "BlueOak-1.0.0", "peer": true, "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=0.12" + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC", + "peer": true + }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, + "license": "MIT", "peer": true, "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -3715,43 +3328,12 @@ "node": ">= 0.4" } }, - "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", - "dev": true, - "peer": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-plugin-solidity": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.4.1.tgz", - "integrity": "sha512-Mq8EtfacVZ/0+uDKTtHZGW3Aa7vEbX/BNx63hmVg6YTiTXSiuKP0amj0G6pGwjmLaOfymWh3QgXEZkjQbU8QRg==", - "dev": true, - "dependencies": { - "@solidity-parser/parser": "^0.18.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "prettier": ">=2.3.0" - } - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "safe-buffer": "^5.1.0" @@ -3778,6 +3360,7 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "inherits": "^2.0.3", @@ -3789,16 +3372,18 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, + "license": "MIT", "peer": true, - "dependencies": { - "picomatch": "^2.2.1" - }, "engines": { - "node": ">=8.10.0" + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/regexp.prototype.flags": { @@ -3841,30 +3426,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "peer": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "node_modules/rlp": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", - "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", - "dev": true, - "peer": true, - "dependencies": { - "bn.js": "^5.2.0" - }, - "bin": { - "rlp": "bin/rlp" - } - }, "node_modules/safe-array-concat": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", @@ -3902,6 +3463,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "peer": true }, "node_modules/safe-regex-test": { @@ -3928,46 +3490,12 @@ "dev": true, "peer": true }, - "node_modules/scrypt-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", - "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", - "dev": true, - "peer": true - }, - "node_modules/secp256k1": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", - "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", - "dev": true, - "hasInstallScript": true, - "peer": true, - "dependencies": { - "elliptic": "^6.5.4", - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, + "license": "BSD-3-Clause", "peer": true, "dependencies": { "randombytes": "^2.1.0" @@ -4005,13 +3533,6 @@ "node": ">= 0.4" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true, - "peer": true - }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -4019,20 +3540,6 @@ "dev": true, "peer": true }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "peer": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" - } - }, "node_modules/sha1-file": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/sha1-file/-/sha1-file-2.0.1.tgz", @@ -4045,6 +3552,31 @@ "node": ">=10" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -4063,6 +3595,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/solc": { "version": "0.8.26", "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", @@ -4175,6 +3721,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "safe-buffer": "~5.2.0" @@ -4194,6 +3741,23 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -4255,18 +3819,19 @@ "node": ">=8" } }, - "node_modules/strip-hex-prefix": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", - "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "is-hex-prefixed": "1.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=6.5.0", - "npm": ">=3" + "node": ">=8" } }, "node_modules/strip-json-comments": { @@ -4274,6 +3839,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=8" @@ -4283,16 +3849,20 @@ } }, "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/surya": { @@ -4331,30 +3901,35 @@ "node >=0.4.0" ] }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "os-tmpdir": "~1.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.4" }, "engines": { - "node": ">=0.6.0" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "peer": true, "dependencies": { - "is-number": "^7.0.0" + "os-tmpdir": "~1.0.2" }, "engines": { - "node": ">=8.0" + "node": ">=0.6.0" } }, "node_modules/toidentifier": { @@ -4390,20 +3965,6 @@ "dev": true, "peer": true }, - "node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "dev": true, - "peer": true - }, - "node_modules/tweetnacl-util": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", - "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==", - "dev": true, - "peer": true - }, "node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -4515,10 +4076,11 @@ } }, "node_modules/undici": { - "version": "5.28.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", - "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "@fastify/busboy": "^2.0.0" @@ -4527,13 +4089,6 @@ "node": ">=14.0" } }, - "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, - "peer": true - }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -4559,6 +4114,7 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/uuid": { @@ -4571,6 +4127,23 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/which-boxed-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", @@ -4626,10 +4199,11 @@ "dev": true }, "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", "dev": true, + "license": "Apache-2.0", "peer": true }, "node_modules/wrap-ansi": { @@ -4649,18 +4223,32 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "peer": true + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } }, "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz", + "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=8.3.0" @@ -4719,6 +4307,7 @@ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "camelcase": "^6.0.0", @@ -4735,6 +4324,7 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=10" @@ -4745,6 +4335,79 @@ } }, "dependencies": { + "@ethereumjs/rlp": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.2.tgz", + "integrity": "sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA==", + "dev": true, + "peer": true + }, + "@ethereumjs/util": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-9.1.0.tgz", + "integrity": "sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog==", + "dev": true, + "peer": true, + "requires": { + "@ethereumjs/rlp": "^5.0.2", + "ethereum-cryptography": "^2.2.1" + }, + "dependencies": { + "@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "peer": true, + "requires": { + "@noble/hashes": "1.4.0" + } + }, + "@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "peer": true + }, + "@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "dev": true, + "peer": true, + "requires": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + } + }, + "@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "dev": true, + "peer": true, + "requires": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + } + }, + "ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "dev": true, + "peer": true, + "requires": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + } + } + }, "@ethersproject/abi": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", @@ -4830,13 +4493,13 @@ } }, "@ethersproject/bytes": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", - "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.8.0.tgz", + "integrity": "sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A==", "dev": true, "peer": true, "requires": { - "@ethersproject/logger": "^5.7.0" + "@ethersproject/logger": "^5.8.0" } }, "@ethersproject/constants": { @@ -4879,9 +4542,9 @@ } }, "@ethersproject/logger": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", - "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.8.0.tgz", + "integrity": "sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA==", "dev": true, "peer": true }, @@ -4896,13 +4559,13 @@ } }, "@ethersproject/properties": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", - "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.8.0.tgz", + "integrity": "sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==", "dev": true, "peer": true, "requires": { - "@ethersproject/logger": "^5.7.0" + "@ethersproject/logger": "^5.8.0" } }, "@ethersproject/rlp": { @@ -4917,17 +4580,17 @@ } }, "@ethersproject/signing-key": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", - "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.8.0.tgz", + "integrity": "sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==", "dev": true, "peer": true, "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", "bn.js": "^5.2.1", - "elliptic": "6.5.4", + "elliptic": "6.6.1", "hash.js": "1.1.7" } }, @@ -4982,18 +4645,95 @@ "dev": true, "peer": true }, - "@metamask/eth-sig-util": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", - "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "peer": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "dev": true, + "peer": true + }, + "ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "peer": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "peer": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "peer": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "peer": true, + "requires": { + "ansi-regex": "^6.2.2" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "peer": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, + "@noble/curves": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.2.tgz", + "integrity": "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==", "dev": true, "peer": true, "requires": { - "ethereumjs-abi": "^0.6.8", - "ethereumjs-util": "^6.2.1", - "ethjs-util": "^0.1.6", - "tweetnacl": "^1.0.3", - "tweetnacl-util": "^0.15.1" + "@noble/hashes": "1.7.2" + }, + "dependencies": { + "@noble/hashes": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz", + "integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==", + "dev": true, + "peer": true + } } }, "@noble/hashes": { @@ -5011,163 +4751,70 @@ "peer": true }, "@nomicfoundation/edr": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.5.2.tgz", - "integrity": "sha512-hW/iLvUQZNTVjFyX/I40rtKvvDOqUEyIi96T28YaLfmPL+3LW2lxmYLUXEJ6MI14HzqxDqrLyhf6IbjAa2r3Dw==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.23.tgz", + "integrity": "sha512-F2/6HZh8Q9RsgkOIkRrckldbhPjIZY7d4mT9LYuW68miwGQ5l7CkAgcz9fRRiurA0+YJhtsbx/EyrD9DmX9BOw==", "dev": true, "peer": true, "requires": { - "@nomicfoundation/edr-darwin-arm64": "0.5.2", - "@nomicfoundation/edr-darwin-x64": "0.5.2", - "@nomicfoundation/edr-linux-arm64-gnu": "0.5.2", - "@nomicfoundation/edr-linux-arm64-musl": "0.5.2", - "@nomicfoundation/edr-linux-x64-gnu": "0.5.2", - "@nomicfoundation/edr-linux-x64-musl": "0.5.2", - "@nomicfoundation/edr-win32-x64-msvc": "0.5.2" + "@nomicfoundation/edr-darwin-arm64": "0.12.0-next.23", + "@nomicfoundation/edr-darwin-x64": "0.12.0-next.23", + "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.23", + "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.23", + "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.23", + "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.23", + "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.23" } }, "@nomicfoundation/edr-darwin-arm64": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.5.2.tgz", - "integrity": "sha512-Gm4wOPKhbDjGTIRyFA2QUAPfCXA1AHxYOKt3yLSGJkQkdy9a5WW+qtqKeEKHc/+4wpJSLtsGQfpzyIzggFfo/A==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.23.tgz", + "integrity": "sha512-Amh7mRoDzZyJJ4efqoePqdoZOzharmSOttZuJDlVE5yy07BoE8hL6ZRpa5fNYn0LCqn/KoWs8OHANWxhKDGhvQ==", "dev": true, "peer": true }, "@nomicfoundation/edr-darwin-x64": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.5.2.tgz", - "integrity": "sha512-ClyABq2dFCsrYEED3/UIO0c7p4H1/4vvlswFlqUyBpOkJccr75qIYvahOSJRM62WgUFRhbSS0OJXFRwc/PwmVg==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.23.tgz", + "integrity": "sha512-9wn489FIQm7m0UCD+HhktjWx6vskZzeZD9oDc2k9ZvbBzdXwPp5tiDqUBJ+eQpByAzCDfteAJwRn2lQCE0U+Iw==", "dev": true, "peer": true }, "@nomicfoundation/edr-linux-arm64-gnu": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.5.2.tgz", - "integrity": "sha512-HWMTVk1iOabfvU2RvrKLDgtFjJZTC42CpHiw2h6rfpsgRqMahvIlx2jdjWYzFNy1jZKPTN1AStQ/91MRrg5KnA==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.23.tgz", + "integrity": "sha512-nlk5EejSzEUfEngv0Jkhqq3/wINIfF2ED9wAofc22w/V1DV99ASh9l3/e/MIHOQFecIZ9MDqt0Em9/oDyB1Uew==", "dev": true, "peer": true }, "@nomicfoundation/edr-linux-arm64-musl": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.5.2.tgz", - "integrity": "sha512-CwsQ10xFx/QAD5y3/g5alm9+jFVuhc7uYMhrZAu9UVF+KtVjeCvafj0PaVsZ8qyijjqVuVsJ8hD1x5ob7SMcGg==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.23.tgz", + "integrity": "sha512-SJuPBp3Rc6vM92UtVTUxZQ/QlLhLfwTftt2XUiYohmGKB3RjGzpgduEFMCA0LEnucUckU6UHrJNFHiDm77C4PQ==", "dev": true, "peer": true }, "@nomicfoundation/edr-linux-x64-gnu": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.5.2.tgz", - "integrity": "sha512-CWVCEdhWJ3fmUpzWHCRnC0/VLBDbqtqTGTR6yyY1Ep3S3BOrHEAvt7h5gx85r2vLcztisu2vlDq51auie4IU1A==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.23.tgz", + "integrity": "sha512-NU+Qs3u7Qt6t3bJFdmmjd5CsvgI2bPPzO31KifM2Ez96/jsXYho5debtTQnimlb5NAqiHTSlxjh/F8ROcptmeQ==", "dev": true, "peer": true }, "@nomicfoundation/edr-linux-x64-musl": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.5.2.tgz", - "integrity": "sha512-+aJDfwhkddy2pP5u1ISg3IZVAm0dO836tRlDTFWtvvSMQ5hRGqPcWwlsbobhDQsIxhPJyT7phL0orCg5W3WMeA==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.23.tgz", + "integrity": "sha512-F78fZA2h6/ssiCSZOovlgIu0dUeI7ItKPsDDF3UUlIibef052GCXmliMinC90jVPbrjUADMd1BUwjfI0Z8OllQ==", "dev": true, "peer": true }, "@nomicfoundation/edr-win32-x64-msvc": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.5.2.tgz", - "integrity": "sha512-CcvvuA3sAv7liFNPsIR/68YlH6rrybKzYttLlMr80d4GKJjwJ5OKb3YgE6FdZZnOfP19HEHhsLcE0DPLtY3r0w==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.23.tgz", + "integrity": "sha512-IfJZQJn7d/YyqhmguBIGoCKjE9dKjbu6V6iNEPApfwf5JyyjHYyyfkLU4rf7hygj57bfH4sl1jtQ6r8HnT62lw==", "dev": true, "peer": true }, - "@nomicfoundation/ethereumjs-common": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz", - "integrity": "sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg==", - "dev": true, - "peer": true, - "requires": { - "@nomicfoundation/ethereumjs-util": "9.0.4" - } - }, - "@nomicfoundation/ethereumjs-rlp": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz", - "integrity": "sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw==", - "dev": true, - "peer": true - }, - "@nomicfoundation/ethereumjs-tx": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz", - "integrity": "sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw==", - "dev": true, - "peer": true, - "requires": { - "@nomicfoundation/ethereumjs-common": "4.0.4", - "@nomicfoundation/ethereumjs-rlp": "5.0.4", - "@nomicfoundation/ethereumjs-util": "9.0.4", - "ethereum-cryptography": "0.1.3" - }, - "dependencies": { - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "peer": true, - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - } - } - }, - "@nomicfoundation/ethereumjs-util": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz", - "integrity": "sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q==", - "dev": true, - "peer": true, - "requires": { - "@nomicfoundation/ethereumjs-rlp": "5.0.4", - "ethereum-cryptography": "0.1.3" - }, - "dependencies": { - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "peer": true, - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - } - } - }, "@nomicfoundation/solidity-analyzer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.2.tgz", @@ -5240,6 +4887,14 @@ "optional": true, "peer": true }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "peer": true + }, "@scure/base": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.7.tgz", @@ -5358,59 +5013,6 @@ "tslib": "^1.9.3" } }, - "@solidity-parser/parser": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.18.0.tgz", - "integrity": "sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA==", - "dev": true - }, - "@types/bn.js": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", - "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", - "dev": true, - "peer": true, - "requires": { - "@types/node": "*" - } - }, - "@types/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", - "dev": true, - "peer": true - }, - "@types/node": { - "version": "22.5.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.1.tgz", - "integrity": "sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==", - "dev": true, - "peer": true, - "requires": { - "undici-types": "~6.19.2" - } - }, - "@types/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", - "dev": true, - "peer": true, - "requires": { - "@types/node": "*" - } - }, - "@types/secp256k1": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz", - "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==", - "dev": true, - "peer": true, - "requires": { - "@types/node": "*" - } - }, "adm-zip": { "version": "0.4.16", "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", @@ -5496,17 +5098,6 @@ "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", "dev": true }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "peer": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -5570,34 +5161,10 @@ "dev": true, "peer": true }, - "base-x": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.10.tgz", - "integrity": "sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==", - "dev": true, - "peer": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "peer": true - }, - "blakejs": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", - "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==", - "dev": true, - "peer": true - }, "bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.5.tgz", + "integrity": "sha512-Vq886eXykuP5E6HcKSSStP3bJgrE6In5WKxVUvJ8XGpWWYs2xZHWqUwzCtGgEtBcxyd57KBFDPFoUfNzdaHCNg==", "dev": true, "peer": true }, @@ -5606,46 +5173,18 @@ "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", "dev": true, - "peer": true, - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "peer": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "peer": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "peer": true, - "requires": { - "has-flag": "^4.0.0" - } - }, + "peer": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -5656,24 +5195,13 @@ } }, "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, "peer": true, "requires": { - "fill-range": "^7.1.1" + "balanced-match": "^1.0.0" } }, "brorand": { @@ -5690,43 +5218,6 @@ "dev": true, "peer": true }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "peer": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", - "dev": true, - "peer": true, - "requires": { - "base-x": "^3.0.2" - } - }, - "bs58check": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", - "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", - "dev": true, - "peer": true, - "requires": { - "bs58": "^4.0.0", - "create-hash": "^1.1.0", - "safe-buffer": "^5.1.2" - } - }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -5734,13 +5225,6 @@ "dev": true, "peer": true }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true, - "peer": true - }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -5775,61 +5259,36 @@ "peer": true }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "peer": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "peer": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "peer": true, "requires": { - "color-name": "1.1.3" + "has-flag": "^4.0.0" } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "peer": true } } }, "chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "peer": true, "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" } }, "ci-info": { @@ -5839,17 +5298,6 @@ "dev": true, "peer": true }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "peer": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -5910,13 +5358,6 @@ "dev": true, "peer": true }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "peer": true - }, "cookie": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", @@ -5924,33 +5365,16 @@ "dev": true, "peer": true }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "peer": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "peer": true, "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, "data-view-buffer": { @@ -6033,16 +5457,23 @@ "peer": true }, "diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "peer": true + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true, "peer": true }, "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", "dev": true, "peer": true, "requires": { @@ -6056,9 +5487,9 @@ }, "dependencies": { "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "version": "4.12.5", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.5.tgz", + "integrity": "sha512-3aRg6/JxfffFD+OlOjOFR3Vo79l39ooBTFucxx+MT3dhCtzn3EmiUPQo+6/OZuI2jbXi3YKgmiTFBgChQMwIRQ==", "dev": true, "peer": true } @@ -6204,9 +5635,9 @@ "dev": true }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "peer": true }, @@ -6223,125 +5654,23 @@ "@scure/bip39": "1.1.1" } }, - "ethereumjs-abi": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", - "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", - "dev": true, - "peer": true, - "requires": { - "bn.js": "^4.11.8", - "ethereumjs-util": "^6.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true - } - } - }, - "ethereumjs-util": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", - "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", - "dev": true, - "peer": true, - "requires": { - "@types/bn.js": "^4.11.3", - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "0.1.6", - "rlp": "^2.2.3" - }, - "dependencies": { - "@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", - "dev": true, - "peer": true, - "requires": { - "@types/node": "*" - } - }, - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true - }, - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "peer": true, - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - } - } - }, - "ethjs-util": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", - "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", - "dev": true, - "peer": true, - "requires": { - "is-hex-prefixed": "1.0.0", - "strip-hex-prefix": "1.0.0" - } - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "peer": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "peer": true, - "requires": { - "to-regex-range": "^5.0.1" - } + "requires": {} }, "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "peer": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, "flat": { @@ -6352,9 +5681,9 @@ "peer": true }, "follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "dev": true, "peer": true }, @@ -6367,6 +5696,17 @@ "is-callable": "^1.1.3" } }, + "foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "peer": true, + "requires": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + } + }, "fp-ts": { "version": "1.19.3", "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", @@ -6386,21 +5726,6 @@ "universalify": "^0.1.0" } }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "peer": true - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true, - "peer": true - }, "function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -6456,28 +5781,18 @@ } }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "peer": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "peer": true, - "requires": { - "is-glob": "^4.0.1" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" } }, "globalthis": { @@ -6516,9 +5831,9 @@ } }, "handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "dev": true, "requires": { "minimist": "^1.2.5", @@ -6529,51 +5844,47 @@ } }, "hardhat": { - "version": "2.22.9", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.9.tgz", - "integrity": "sha512-sWiuI/yRdFUPfndIvL+2H18Vs2Gav0XacCFYY5msT5dHOWkhLxESJySIk9j83mXL31aXL8+UMA9OgViFLexklg==", + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.29.0.tgz", + "integrity": "sha512-tsj5mCSjDCFOhGfBl4vwqDEcwdlES9VUzRWfdrwvEVhus6D8W6u+WfUKRLLwFhKGS/8lKPoXGsjYWPXl3CCpOg==", "dev": true, "peer": true, "requires": { + "@ethereumjs/util": "^9.1.0", "@ethersproject/abi": "^5.1.2", - "@metamask/eth-sig-util": "^4.0.0", - "@nomicfoundation/edr": "^0.5.2", - "@nomicfoundation/ethereumjs-common": "4.0.4", - "@nomicfoundation/ethereumjs-tx": "5.0.4", - "@nomicfoundation/ethereumjs-util": "9.0.4", + "@nomicfoundation/edr": "0.12.0-next.23", "@nomicfoundation/solidity-analyzer": "^0.1.0", "@sentry/node": "^5.18.1", - "@types/bn.js": "^5.1.0", - "@types/lru-cache": "^5.1.0", "adm-zip": "^0.4.16", "aggregate-error": "^3.0.0", "ansi-escapes": "^4.3.0", "boxen": "^5.1.2", - "chalk": "^2.4.2", - "chokidar": "^3.4.0", + "chokidar": "^4.0.0", "ci-info": "^2.0.0", "debug": "^4.1.1", "enquirer": "^2.3.0", "env-paths": "^2.2.0", "ethereum-cryptography": "^1.0.3", - "ethereumjs-abi": "^0.6.8", - "find-up": "^2.1.0", + "find-up": "^5.0.0", "fp-ts": "1.19.3", "fs-extra": "^7.0.1", - "glob": "7.2.0", "immutable": "^4.0.0-rc.12", "io-ts": "1.10.4", + "json-stream-stringify": "^3.1.4", "keccak": "^3.0.2", "lodash": "^4.17.11", + "micro-eth-signer": "^0.14.0", "mnemonist": "^0.38.0", - "mocha": "^10.0.0", + "mocha": "^11.1.0", "p-map": "^4.0.0", + "picocolors": "^1.1.0", "raw-body": "^2.4.1", "resolve": "1.17.0", "semver": "^6.3.0", "solc": "0.8.26", "source-map-support": "^0.5.13", "stacktrace-parser": "^0.1.10", + "tinyglobby": "^0.2.6", "tsort": "0.0.1", "undici": "^5.14.0", "uuid": "^8.3.2", @@ -6596,9 +5907,9 @@ "dev": true }, "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "peer": true }, @@ -6632,18 +5943,6 @@ "has-symbols": "^1.0.3" } }, - "hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, - "peer": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - } - }, "hash.js": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", @@ -6729,9 +6028,9 @@ } }, "immutable": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", - "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.9.tgz", + "integrity": "sha512-ObHy4YN7ycwZOUCLI1/6svfyAFu7vL8RhAvVu/bh/RZW9EPlOyDaQ9jDQWCtdqzaXUjgXZCW1migtHE7YI7UGQ==", "dev": true, "peer": true }, @@ -6742,17 +6041,6 @@ "dev": true, "peer": true }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "peer": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -6800,16 +6088,6 @@ "has-bigints": "^1.0.1" } }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "peer": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, "is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -6844,49 +6122,18 @@ "has-tostringtag": "^1.0.0" } }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "peer": true - }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "peer": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-hex-prefixed": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", - "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", - "dev": true, - "peer": true - }, "is-negative-zero": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "peer": true - }, "is-number-object": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", @@ -6896,6 +6143,13 @@ "has-tostringtag": "^1.0.0" } }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "peer": true + }, "is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -6977,6 +6231,24 @@ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "peer": true + }, + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "peer": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", @@ -6985,15 +6257,22 @@ "peer": true }, "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, "peer": true, "requires": { "argparse": "^2.0.1" } }, + "json-stream-stringify": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.1.7.tgz", + "integrity": "sha512-F4MWetLtY42YMaAKw5cV4e47zMD5aOT+tjjQWjX18ACtdkQ5Y/vrcfbcQ107Rh+MXjOCIx4KhW0wPmOvG8iQ5w==", + "dev": true, + "peer": true + }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -7017,62 +6296,31 @@ } }, "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "peer": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "peer": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "peer": true, "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "peer": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "peer": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "peer": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "peer": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "peer": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" } }, "lru_map": { @@ -7082,17 +6330,12 @@ "dev": true, "peer": true }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "peer": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } + "peer": true }, "memorystream": { "version": "0.3.1", @@ -7101,6 +6344,46 @@ "dev": true, "peer": true }, + "micro-eth-signer": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/micro-eth-signer/-/micro-eth-signer-0.14.0.tgz", + "integrity": "sha512-5PLLzHiVYPWClEvZIXXFu5yutzpadb73rnQCpUqIHu3No3coFuWQNfE5tkBQJ7djuLYl6aRLaS0MgWJYGoqiBw==", + "dev": true, + "peer": true, + "requires": { + "@noble/curves": "~1.8.1", + "@noble/hashes": "~1.7.1", + "micro-packed": "~0.7.2" + }, + "dependencies": { + "@noble/hashes": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz", + "integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==", + "dev": true, + "peer": true + } + } + }, + "micro-packed": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/micro-packed/-/micro-packed-0.7.3.tgz", + "integrity": "sha512-2Milxs+WNC00TRlem41oRswvw31146GiSaoCT7s3Xi2gMUglW5QBeqlQaZeHr5tJx9nm3i57LNXPqxOOaWtTYg==", + "dev": true, + "peer": true, + "requires": { + "@scure/base": "~1.2.5" + }, + "dependencies": { + "@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "dev": true, + "peer": true + } + } + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -7116,13 +6399,13 @@ "peer": true }, "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "peer": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.2" } }, "minimist": { @@ -7131,6 +6414,13 @@ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true }, + "minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "peer": true + }, "mnemonist": { "version": "0.38.5", "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", @@ -7142,181 +6432,41 @@ } }, "mocha": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", - "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.8.0.tgz", + "integrity": "sha512-VyCeUdGN3A9lmCTTgG4yuvY9ixxaDk+xt2R/7/+1AP6EqNG+G9OKkzBwhVtVYoNX8YsxNSgAl8mOv3IAeOpFbw==", "dev": true, "peer": true, "requires": { - "ansi-colors": "^4.1.3", "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", + "chokidar": "^4.0.1", "debug": "^4.3.5", - "diff": "^5.2.0", + "diff": "^7.0.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", - "glob": "^8.1.0", + "glob": "^10.4.5", "he": "^1.2.0", + "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", + "minimatch": "^9.0.5", "ms": "^2.1.3", + "picocolors": "^1.1.1", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" }, "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "peer": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "peer": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "peer": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "peer": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dev": true, - "peer": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "peer": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "peer": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "peer": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "peer": true - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "peer": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "peer": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "peer": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "peer": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "peer": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "peer": true } } }, @@ -7341,16 +6491,9 @@ "peer": true }, "node-gyp-build": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", - "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", - "dev": true, - "peer": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", "dev": true, "peer": true }, @@ -7385,16 +6528,6 @@ "dev": true, "peer": true }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "peer": true, - "requires": { - "wrappy": "1" - } - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -7403,23 +6536,23 @@ "peer": true }, "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "peer": true, "requires": { - "p-try": "^1.0.0" + "yocto-queue": "^0.1.0" } }, "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "peer": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "^3.0.2" } }, "p-map": { @@ -7432,24 +6565,24 @@ "aggregate-error": "^3.0.0" } }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, "peer": true }, "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "peer": true }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "peer": true }, @@ -7460,24 +6593,28 @@ "dev": true, "peer": true }, - "pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "peer": true, "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "peer": true + }, "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "peer": true }, @@ -7487,23 +6624,6 @@ "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "dev": true }, - "prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", - "dev": true, - "peer": true - }, - "prettier-plugin-solidity": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.4.1.tgz", - "integrity": "sha512-Mq8EtfacVZ/0+uDKTtHZGW3Aa7vEbX/BNx63hmVg6YTiTXSiuKP0amj0G6pGwjmLaOfymWh3QgXEZkjQbU8QRg==", - "dev": true, - "requires": { - "@solidity-parser/parser": "^0.18.0", - "semver": "^7.5.4" - } - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -7540,14 +6660,11 @@ } }, "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, - "peer": true, - "requires": { - "picomatch": "^2.2.1" - } + "peer": true }, "regexp.prototype.flags": { "version": "1.5.2", @@ -7577,27 +6694,6 @@ "path-parse": "^1.0.6" } }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "peer": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "rlp": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", - "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", - "dev": true, - "peer": true, - "requires": { - "bn.js": "^5.2.0" - } - }, "safe-array-concat": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", @@ -7635,31 +6731,6 @@ "dev": true, "peer": true }, - "scrypt-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", - "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", - "dev": true, - "peer": true - }, - "secp256k1": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", - "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", - "dev": true, - "peer": true, - "requires": { - "elliptic": "^6.5.4", - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0" - } - }, - "semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true - }, "serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", @@ -7696,13 +6767,6 @@ "has-property-descriptors": "^1.0.2" } }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true, - "peer": true - }, "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -7710,17 +6774,6 @@ "dev": true, "peer": true }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "peer": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, "sha1-file": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/sha1-file/-/sha1-file-2.0.1.tgz", @@ -7730,6 +6783,23 @@ "hasha": "^5.2.0" } }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "peer": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "peer": true + }, "side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -7742,6 +6812,13 @@ "object-inspect": "^1.13.1" } }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "peer": true + }, "solc": { "version": "0.8.26", "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", @@ -7850,6 +6927,18 @@ "strip-ansi": "^6.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "peer": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -7893,14 +6982,14 @@ "ansi-regex": "^5.0.1" } }, - "strip-hex-prefix": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", - "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "peer": true, "requires": { - "is-hex-prefixed": "1.0.0" + "ansi-regex": "^5.0.1" } }, "strip-json-comments": { @@ -7911,13 +7000,13 @@ "peer": true }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "peer": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } }, "surya": { @@ -7952,24 +7041,25 @@ "integrity": "sha512-IsFisGgDKk7qzK9erMIkQe/XwiSUdac7z3wYOsjcLkhPBy3k1SlvLoIh2dAHIlEpgA971CgguMrx9z8fFg7tSA==", "dev": true }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "peer": true, "requires": { - "os-tmpdir": "~1.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.4" } }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "peer": true, "requires": { - "is-number": "^7.0.0" + "os-tmpdir": "~1.0.2" } }, "toidentifier": { @@ -7999,20 +7089,6 @@ "dev": true, "peer": true }, - "tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "dev": true, - "peer": true - }, - "tweetnacl-util": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", - "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==", - "dev": true, - "peer": true - }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -8091,22 +7167,15 @@ } }, "undici": { - "version": "5.28.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", - "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", "dev": true, "peer": true, "requires": { "@fastify/busboy": "^2.0.0" } }, - "undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, - "peer": true - }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -8135,6 +7204,16 @@ "dev": true, "peer": true }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "peer": true, + "requires": { + "isexe": "^2.0.0" + } + }, "which-boxed-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", @@ -8178,9 +7257,9 @@ "dev": true }, "workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", "dev": true, "peer": true }, @@ -8195,17 +7274,22 @@ "strip-ansi": "^6.0.0" } }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "peer": true + "peer": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } }, "ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz", + "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==", "dev": true, "peer": true, "requires": {} From 523746d97baa9d56d3c470d50cce0175eacccbbc Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 15:46:38 +0200 Subject: [PATCH 45/47] docs: add a table of contents to doc/README.md and fix the orphaned heading levels --- CHANGELOG.md | 4 +++- doc/README.md | 49 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccbfc7a..54fca5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,11 +34,13 @@ Reference: [keepachangelog.com/en/1.1.0/](https://keepachangelog.com/en/1.1.0/) > forge fmt +- Package + - Run `npm audit fix` + - Documentation - Perform a code coverage and update the files in the corresponding directory [./doc/coverage](./doc/coverage) (`forge coverage --report lcov --report-file /tmp/lcov-full.info`, then `lcov --extract /tmp/lcov-full.info 'src/*' -o doc/coverage/lcov.info` and `genhtml doc/coverage/lcov.info --output-directory doc/coverage/coverage`; the `src/*` filter keeps `test/` and `script/` out of the published figure) - Perform an audit with several audit tools (Aderyn and Slither), update the report in the corresponding directory [./doc/audits/tools](./doc/audits/tools) - Update surya doc by running the 3 scripts in [./doc/script](./doc/script) - - Update changelog ## v0.4.0 diff --git a/doc/README.md b/doc/README.md index 23ceac7..f9dcab3 100644 --- a/doc/README.md +++ b/doc/README.md @@ -2,6 +2,37 @@ > This project has not been audited yet, please use at your own risk. For any questions, please contact [admin@cmta.ch](mailto:admin@cmta.ch). +## Table of contents + +- [Two ways to manage documents](#two-ways-to-manage-documents) +- [Flexible access control](#flexible-access-control) +- [Why not reuse RuleEngine's ERC-3643 compliance module?](#why-not-reuse-ruleengines-erc-3643-compliance-module) +- [Events](#events) +- [Integration with CMTAT](#integration-with-cmtat) + - [Topology](#topology) + - [Writing a document](#writing-a-document) + - [Reading a document](#reading-a-document) + - [Wiring and the full call flow](#wiring-and-the-full-call-flow) +- [Architecture](#architecture) +- [Version (ERC-8303)](#version-erc-8303) + - [ERC-165: what the engine advertises](#erc-165-what-the-engine-advertises) +- [Schema](#schema) + - [Inheritance](#inheritance) + - [Graph](#graph) +- [Surya Description Report](#surya-description-report) + - [Contracts Description Table](#contracts-description-table) + - [Interfaces](#interfaces) + - [Legend](#legend) +- [Gasless support (ERC-2771)](#gasless-support-erc-2771) +- [Dependencies](#dependencies) + - [Version compatibility](#version-compatibility) +- [Tools](#tools) + - [Formatting (forge fmt)](#formatting-forge-fmt) + - [Static analysis](#static-analysis) + - [Surya](#surya) + - [Foundry](#foundry) +- [Intellectual property](#intellectual-property) + The `DocumentEngine` is an external contract to manage documents through [*ERC-1643*](https://github.com/ethereum/EIPs/issues/1643), a proposed standard for managing documents on-chain. [ERC-1400](https://github.com/ethereum/eips/issues/1411) from Polymath builds on it. The DocumentEngine is meant to be used by other smart contracts, e.g. a CMTAT token, to store documents on their behalf. @@ -36,7 +67,7 @@ Using an external contract for your smart contract provides two advantages: - Reduce code size of your smart contract - Allow to manage documents for several different smart contracts -### Two ways to manage documents +## Two ways to manage documents The engine supports **two management paths** at the same time: @@ -66,7 +97,7 @@ function removeDocument(bytes32 name_) external; > This mirrors the RuleEngine *binding* pattern without reusing its `ERC3643ComplianceExtendedModule` — see [Why not reuse RuleEngine's ERC-3643 compliance module?](#why-not-reuse-ruleengines-erc-3643-compliance-module) below. -### Flexible access control +## Flexible access control Following the CMTAT / [RuleEngine](https://github.com/CMTA/RuleEngine) pattern, the restricted functions do not hardcode a check. They carry a **modifier** (`onlyDocumentManager` / `onlyBoundToken`) that delegates to an **overridable `internal virtual` authorization hook**: @@ -87,7 +118,7 @@ function _authorizeBoundTokenDocumentManagement() internal view virtual override This separates the document-management implementation from the authorization logic: a subclass changes *who* is authorized by overriding the hook, never by touching the management functions. -### Why not reuse RuleEngine's ERC-3643 compliance module? +## Why not reuse RuleEngine's ERC-3643 compliance module? CMTA's [RuleEngine](https://github.com/CMTA/RuleEngine) (v3) ships an `ERC3643ComplianceExtendedModule` that offers a ready-made token-binding registry (`bindToken` / `unbindToken` / `isTokenBound` / `getTokenBounds`). It is tempting to reuse it for the bound-token path, but we deliberately do **not**, because that module is an **`IERC3643Compliance`** — a *transfer-compliance* contract. @@ -104,15 +135,15 @@ A document engine has **nothing to do with token transfers**, so these would hav The binding concept we actually need is tiny — "is this caller a token allowed to manage its own documents?" — so we implement just that: a **single allowlist** in `TokenBindingModule`, shared by both deployments and gated by each one's document-management hook. It is deliberately **not** a role: there is no `TOKEN_CONTRACT_ROLE`, and `DocumentEngineOwnable` uses the same allowlist rather than a separate owner-managed one. This keeps the engine's surface honest and minimal while still mirroring the RuleEngine binding pattern; the RuleEngine submodule is kept as a reference for that pattern. -### Events +## Events This engine is a **shared, multi-token** document manager, so — per the ERC-1643 ["Emission Responsibility"](./ERCSpecification/erc-1643.md) rules — it emits **only** the address-carrying extension events `DocumentUpdatedForSubject(address indexed subject, …)` / `DocumentRemovedForSubject(…)`, and **not** the base `DocumentUpdated` / `DocumentRemoved` events. The base events carry no address and so cannot identify which token contract a change belongs to; they are the responsibility of the token contract that exposes ERC-1643 to consumers (it re-emits them when delegating). See the [Multi-Subject Document Management draft](./ERCSpecification/erc-draft_multi_document_management.md) and the `IERC1643MultiDocument` extension. -### Integration with CMTAT +## Integration with CMTAT Since CMTAT v3, the shipped standalone tokens store documents on-chain (`DocumentERC1643Module`) and do not consume an external engine through their constructor. To use this engine, a CMTAT token relies on the `DocumentEngineModule` and is wired at runtime with `setDocumentEngine(engine)`; reads/writes are then forwarded to the engine keyed by the token address. -#### Topology +### Topology One engine serves a whole fleet of tokens. Each token keeps its own document namespace, keyed by its address, and can never reach another token's: @@ -120,19 +151,19 @@ One engine serves a whole fleet of tokens. Each token keeps its own document nam _Diagram source: `doc/img/cmtat-integration-architecture.puml`._ -#### Writing a document +### Writing a document ![Writing a document through a CMTAT token](./img/cmtat-write-simple.png) _Diagram source: `doc/img/cmtat-write-simple.puml`._ -#### Reading a document +### Reading a document ![Reading a document from a CMTAT token or the engine](./img/cmtat-read-simple.png) _Diagram source: `doc/img/cmtat-read-simple.puml`._ -#### Wiring and the full call flow +### Wiring and the full call flow Two independent steps wire a token to the engine, and they are easy to get half right: `bindToken(token)` on the **engine** authorises the token to use the single-argument ERC-1643 functions, while `setDocumentEngine(engine)` on the **token** tells it where to forward. Bind without wiring and the token has nowhere to send; wire without binding and the forwarded call reverts `NotBoundToken`. From 3059d0e2f46e9763a357a199c4d32438d4794398 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 15:53:16 +0200 Subject: [PATCH 46/47] =?UTF-8?q?docs:=20state=20the=20verified=20CMTAT=20?= =?UTF-8?q?compatibility=20range=20(rc2=E2=80=93rc3)=20instead=20of=20a=20?= =?UTF-8?q?single=20pinned=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- README.md | 9 ++++++++- doc/README.md | 19 ++++++++++--------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54fca5b..0335095 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,7 +52,7 @@ Targets **CMTAT `v3.3.0-rc3`** — see the [compatibility matrix](./doc/README.m ### Changed - **Dependencies** - - Upgrade CMTAT `v2.5.0-rc0` → [`v3.3.0-rc3`](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) (`lib/CMTAT` → `658672f190d56d3f61663a7d6d51962b8980df70`). Development passed through `v3.3.0-rc1` and `v3.3.0-rc2`. rc1 is **not** compatible with the code as shipped here, because it declares neither the ERC-1643 errors nor the flat `getDocument` return (see below); rc2 and rc3 are interchangeable for this engine — between them, the whole document surface (`draft-IERC1643.sol`, `IDocumentEngine.sol`, `DocumentEngineModule.sol`, `DocumentERC1643Module.sol`) changed only its pragma, `^0.8.20` → `^0.8.24`. + - Upgrade CMTAT `v2.5.0-rc0` → [`v3.3.0-rc3`](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) (`lib/CMTAT` → `658672f190d56d3f61663a7d6d51962b8980df70`). Development passed through `v3.3.0-rc1` and `v3.3.0-rc2`. rc1 is **not** compatible with the code as shipped here, because it declares neither the ERC-1643 errors nor the flat `getDocument` return (see below); rc2 and rc3 are interchangeable for this engine — between them, the whole document surface (`draft-IERC1643.sol`, `IDocumentEngine.sol`, `DocumentEngineModule.sol`, `DocumentERC1643Module.sol`) changed only its pragma, `^0.8.20` → `^0.8.24`. Both are therefore listed as the supported range, verified by building and running the full suite against each (74/74 on both). Nothing below rc2 works: `v3.0.0`/`v3.1.0`/`v3.2.0` return a `Document` struct and declare no interface errors, and they predate CMTAT's token-side `DocumentEngineModule` entirely. - Upgrade OpenZeppelin Contracts (and Contracts Upgradeable) `v5.0.2` → [`v5.7.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.7.0). `v5.7.0` deprecates `EnumerableSet.at()` in favour of `pos()` (the old name clashes with a keyword scheduled for Solidity); `at()` remains as a forwarding alias, and this engine has no call sites either way. The only exposure is inherited — `AccessControlEnumerable.getRoleMember` switched to `pos()` internally, with no change to its signature, selector or behaviour. Verified: `DocumentEngine`'s runtime code is **byte-identical** across `v5.6.1` and `v5.7.0` (8436 bytes; only the CBOR metadata trailer moves, because the source text of `AccessControlEnumerable.sol` changed), and `DocumentEngineOwnable`'s bytecode is unchanged including metadata. - Add [CMTA/RuleEngine](https://github.com/CMTA/RuleEngine) [`v3.0.0-rc5`](https://github.com/CMTA/RuleEngine/releases/tag/v3.0.0-rc5) as a submodule (binding-pattern reference; see [Why not reuse RuleEngine's compliance module?](./doc/README.md#why-not-reuse-ruleengines-erc-3643-compliance-module) — its `ERC3643ComplianceExtendedModule` is not reused) - `foundry.lock` now records every submodule by tag; all five entries had gone stale since `v0.3.0`. diff --git a/README.md b/README.md index c30d03a..218361f 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,14 @@ The forwarder enables ERC-2771 gasless calls and is **immutable**; pass `address | Reporting a vulnerability | [`SECURITY.md`](./SECURITY.md) | | Diagrams (Surya, PlantUML) | [`doc/surya/`](./doc/surya), [`doc/img/`](./doc/img) | -Current version `0.4.0`, built against CMTAT `v3.3.0-rc3` and OpenZeppelin `v5.7.0` — see the [version compatibility matrix](./doc/README.md#version-compatibility), since this engine is supported only against the CMTAT release it was built for. +## Compatibility + +| DocumentEngine | Compatible CMTAT | Tested against | +| -------------- | ---------------- | -------------- | +| **v0.4.0** (current) | `v3.3.0-rc2` – `v3.3.0-rc3` | v3.3.0-rc3 | +| v0.3.0 and earlier | v2.5.0-rc0 | v2.5.0-rc0 | + +The range is closed at both ends on purpose. CMTAT's `IERC1643` changed shape inside a single minor line — `getDocument` returns a `Document` struct up to `v3.3.0-rc1` and the three flat values from `v3.3.0-rc2` — so anything below rc2 does not compile, and a newer CMTAT is not assumed compatible until it has been tested. Full detail, including the Solidity and OpenZeppelin columns: [version compatibility](./doc/README.md#version-compatibility). ## Intellectual property diff --git a/doc/README.md b/doc/README.md index f9dcab3..a6cda67 100644 --- a/doc/README.md +++ b/doc/README.md @@ -384,19 +384,20 @@ The toolchain includes the following components, where the versions are the late ### Version compatibility -Each release of this engine is built and tested against one CMTAT release. CMTAT's `IERC1643` is not stable across its own release candidates, so pairing a version of this engine with a different CMTAT than the one below is not supported. +Each release of this engine is tested against one CMTAT release and is supported across a **verified, bounded range** of them. The range is deliberately closed at both ends rather than written as `≥ minimum`: CMTAT's `IERC1643` has changed shape *within* a single minor line — `getDocument` returned a `Document` struct in `v3.0.0`, `v3.1.0`, `v3.2.0` and `v3.3.0-rc1`, and only became the three flat values at `v3.3.0-rc2` — so a future CMTAT release cannot be assumed compatible until it has been built and tested against. -| DocumentEngine | CMTAT | Solidity / `evm_version` | OpenZeppelin | `getDocument` returns | -| -------------- | ----- | ------------------------ | ------------ | --------------------- | -| **v0.4.0** (current) | [v3.3.0-rc3](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) | `0.8.34` / `prague` | v5.7.0 | `(string, bytes32, uint256)` | -| v0.3.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | -| v0.2.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | -| v0.1.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | +| DocumentEngine | Compatible CMTAT | Tested against | Solidity / `evm_version` | OpenZeppelin | `getDocument` returns | +| -------------- | ---------------- | -------------- | ------------------------ | ------------ | --------------------- | +| **v0.4.0** (current) | `v3.3.0-rc2` – [`v3.3.0-rc3`](https://github.com/CMTA/CMTAT/releases/tag/v3.3.0-rc3) | v3.3.0-rc3 | `0.8.34` / `prague` | v5.7.0 | `(string, bytes32, uint256)` | +| v0.3.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) (range not established) | v2.5.0-rc0 | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | +| v0.2.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) (range not established) | v2.5.0-rc0 | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | +| v0.1.0 | [v2.5.0-rc0](https://github.com/CMTA/CMTAT/releases/tag/v2.5.0-rc0) (range not established) | v2.5.0-rc0 | `0.8.26` / `cancun` | v5.0.2 | `(string, bytes32, uint256)` | Notes on the CMTAT v2 → v3 jump at `v0.4.0`: -- **CMTAT `v3.3.0-rc1` is not supported.** It is the one release in which `IERC1643.getDocument` returns a `Document` struct rather than the three flat values; `v3.3.0-rc2` reverted that and `v3.3.0-rc3` keeps the flat return. rc1 also does not declare `ERC1643InvalidName` / `ERC1643MissingDocument` on the interface. Building this engine against rc1 fails to compile. -- **`v3.3.0-rc2` → `v3.3.0-rc3` is a no-op for this engine.** The only change to the document surface (`draft-IERC1643.sol`, `IDocumentEngine.sol`, `DocumentEngineModule.sol`, `DocumentERC1643Module.sol`) is a pragma bump from `^0.8.20` to `^0.8.24`; the interface, the errors and the `getDocument` return shape are unchanged. +- **No CMTAT below `v3.3.0-rc2` is supported, including the v3.0–v3.2 finals.** `v3.0.0`, `v3.1.0` and `v3.2.0` return a `Document` struct from `IERC1643.getDocument` and declare neither `ERC1643InvalidName` nor `ERC1643MissingDocument` on the interface, so this engine does not compile against them; they also predate CMTAT's token-side `DocumentEngineModule`, which first appears at `v3.3.0-rc1`, so a token on those versions has no supported way to consume an external engine at all. +- **CMTAT `v3.3.0-rc1` is not supported either.** It is the one release in which `IERC1643.getDocument` returns a `Document` struct rather than the three flat values; `v3.3.0-rc2` reverted that and `v3.3.0-rc3` keeps the flat return. rc1 also does not declare `ERC1643InvalidName` / `ERC1643MissingDocument` on the interface. Building this engine against rc1 fails to compile. +- **`v3.3.0-rc2` → `v3.3.0-rc3` is a no-op for this engine**, which is why the supported range spans both. The only change to the document surface (`draft-IERC1643.sol`, `IDocumentEngine.sol`, `DocumentEngineModule.sol`, `DocumentERC1643Module.sol`) is a pragma bump from `^0.8.20` to `^0.8.24`; the interface, the errors and the `getDocument` return shape are unchanged. Verified by building and running the full suite against rc2 as well as rc3 — 74/74 in both. - The `IERC1643` import path moved in CMTAT v3, from `CMTAT/interfaces/engine/draft-IERC1643.sol` to `CMTAT/interfaces/tokenization/draft-IERC1643.sol`. - Document names became `bytes32` in CMTAT v3 (they were `string` up to v2.5.0-rc0). - Two different Solidity floors apply from `v0.4.0` on, and the sources declare the lower of them: From 155a7feff7dcba0587d1258efaf9f7e45cca6fef Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Mon, 17 Aug 2026 16:13:33 +0200 Subject: [PATCH 47/47] Update doc --- README.md | 6 +++--- doc/README.md | 12 ++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 218361f..02920bc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # DocumentEngine (ERC-1643) -> This project has not been audited yet, please use at your own risk. For any questions, please contact [admin@cmta.ch](mailto:admin@cmta.ch). - A standalone contract that stores **[ERC-1643](https://github.com/ethereum/EIPs/issues/1643) documents on-chain on behalf of other contracts** — typically [CMTAT](https://github.com/CMTA/CMTAT) tokens. One engine serves a whole fleet: each subject gets its own namespace, keyed by its address, and can never reach another's. A document is `{ string uri, bytes32 documentHash, uint256 lastModified }`, addressed by a `bytes32` name. @@ -14,6 +12,8 @@ Why use an external engine rather than storing documents in the token: **Specification and full reference: [`doc/README.md`](./doc/README.md).** +> This project has not been audited yet, please use at your own risk. For any questions, please contact [admin@cmta.ch](mailto:admin@cmta.ch). + ## Quick start ```bash @@ -118,4 +118,4 @@ The range is closed at both ends on purpose. CMTAT's `IERC1643` changed shape in ## Intellectual property -The code is copyright (c) Capital Market and Technology Association, 2018-2024, and is released under [Mozilla Public License 2.0](https://github.com/CMTA/CMTAT/blob/master/LICENSE.md). +The code is copyright (c) Capital Market and Technology Association, 2018-2026, and is released under [Mozilla Public License 2.0](https://github.com/CMTA/CMTAT/blob/master/LICENSE.md). diff --git a/doc/README.md b/doc/README.md index a6cda67..9d465db 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,9 +1,15 @@ # DocumentEngine (ERC-1643) - Specification + +The `DocumentEngine` is an external contract to manage documents through [*ERC-1643*](https://github.com/ethereum/EIPs/issues/1643), a proposed standard for managing documents on-chain. [ERC-1400](https://github.com/ethereum/eips/issues/1411) from Polymath builds on it. + +The DocumentEngine is meant to be used by other smart contracts, e.g. a CMTAT token, to store documents on their behalf. + > This project has not been audited yet, please use at your own risk. For any questions, please contact [admin@cmta.ch](mailto:admin@cmta.ch). ## Table of contents +- [Introduction](#introduction) - [Two ways to manage documents](#two-ways-to-manage-documents) - [Flexible access control](#flexible-access-control) - [Why not reuse RuleEngine's ERC-3643 compliance module?](#why-not-reuse-ruleengines-erc-3643-compliance-module) @@ -33,9 +39,7 @@ - [Foundry](#foundry) - [Intellectual property](#intellectual-property) -The `DocumentEngine` is an external contract to manage documents through [*ERC-1643*](https://github.com/ethereum/EIPs/issues/1643), a proposed standard for managing documents on-chain. [ERC-1400](https://github.com/ethereum/eips/issues/1411) from Polymath builds on it. - -The DocumentEngine is meant to be used by other smart contracts, e.g. a CMTAT token, to store documents on their behalf. +## Introduction The ERC-1643 defines a document with three attributes: @@ -557,4 +561,4 @@ $ cast --help ## Intellectual property -The code is copyright (c) Capital Market and Technology Association, 2018-2024, and is released under [Mozilla Public License 2.0](https://github.com/CMTA/CMTAT/blob/master/LICENSE.md). +The code is copyright (c) Capital Market and Technology Association, 2018-2026, and is released under [Mozilla Public License 2.0](https://github.com/CMTA/CMTAT/blob/master/LICENSE.md).

` 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⁢aR)WB+Fr!pvY(}4|;_RC@&bzzG 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 G1pF@`-Wu@$ delta 138217 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�|*_9CE)roO6yR<1pO`b#J zXrq0}dt=(;{e2%B9Ur{u}ytqdM-J1>QP%Tzib zru9C5B>%%oV^!mM|EtR%i|`0Dy2S5kBZY?R4fVe8w(Y z7|Zn8F=BIjaO&<;-x~Ys6#v!Gz@FRxPNbfWigi}Iai4i_9a;Mtr=HenD~EC%s$U|zlvmNA?)_UerD=JY+!=#oHPVZy)t(-7i5G4qrAlbKofr|jsyr*k`(s|> zkdqzVI`R|^)uYjkX*1#YndUvzq$;z#fuWx1@83NZJe01U(l^SMI_$B-R64fHHmJ*b zan&}J3Dah!a-sgD%<8XmrL7UhhHS+1eFbfU<~q-+e=RfKZ9G3KbJ|q;xvPJGKhZ}G zeo#Zh?QvE<>DTfqSC-E$RDQ_z-txAThBvDDG!lAXFW`l=-syiYWXDWQ7$u)83+m+d ze0$0+DA=wb2|r3xx2cFT9iRR>CfY-gWg;<+R1iR8&DFWk|77!TfzALWtNgBgN@b$u zJTxvrh{$IYP)xrFnSg{(mHx}QRaHm>l0I@T7O;-K>Js&)ReEzKpHS&jk%b2r$!7j&Hv z&d?qATEBj^H7bv~c{7M#Dd}XMLB?YIq8AwM!f}zgq`Zv5lGiK zS1v1=o}p@KObmKe64tm~EaoHrU|A0tsTh*beWXYvU9b9ZLzmxspnmLKgvPi=y1QtX zkow4BCim0&aYMdYL%y*?Ds_`R!S4@hyqxh?mK8NJF)=YSbC%^n;{D2%@-ADez*Z6| zK-g+`d=qQ@@ys53WPc5EnORwFvc78wvrR)af9uGWo$rC1urJ|T!O)$$xe1$KZcl?7 zv3cXhM_smXXdvl4Wpab|=QG7!(;@Mbs7$%UX}fd>u;Lg{g)n9%YrWv=6T2mAaq5R84|R6tzMgx)ZAu6EHu&?Ia*x9 zKJQJNp(fQ$pDY?6==!GPr43mVxX=Ce))=T8(WO>PHo9gkx^zygwO%$xjB{#%%Ioit z*HzUo3xZ`I&!*W`AW?;4(~(7->;(+apqN3q1|B`YEpc ze}JQ_x3nu@PtD8>*?dfbjUGdd&qHkrRWW3O#|3W^Z^iJy&2!II{+LtQx0SU-s_m19 z64cv_lzI*WlCAiPsy2y)=@ zgCVIs_NbDdI=Y31nrijK?tas(b@9i!J#S%tcc^xvQ&M!giwO-;T&ULZ<5rUWFUeb!L=AH9F<-0WcI#7k+GVSk-vtOpx$@oc zbuEnt>1nPo94+&~#bgpFGpq#mF&7$7Q*sA1T>$K#oq<@4X=nTx^GQg8WMTzTh zi`BE@;$p}<@fEL^FJE@@g&i#BfB-c{2H%mxJI0^8oxfO0u6d?4Z zKW6DWZwza{WA&tQcdPy839s#;@1-?5>nI=TptPzmN766vinz8rZC}DvgpBWOc~S7` z_N`)+XZ~DMk&YdBbZirGmVR$%#Tj~lPAMT<$3yGj2|NWC$^FkqS+1HD; zr5(+@DPYZ8Qs(2T$z;Kk30==yBIh(pRd@kXxRbl+ldP1Kl#-H?ScnK)8qjdz_aY(p zEADr6E3X1`D*oTgY0Ktl(`9}|vpl`)k94P+WV3n_taqRARNSrZ#Ff4gG07u02cte` zrY%zUEj>M>%1v|96uv1?hjhBRyYt^5XJ?BWjRo}N#b0M#tW`2s6|ne-~VI#6%C)yLWOHDYZ5jeYbqV{bg;MgML2n9yN-I8c(f*z zv56>P71tY z>V_zrH?}HVld8Acza*L%6IBo z_@V0tb7u!kpJr!&B_N`N36Z+k!5 z$SU9BeqH5hu|{!MP^hzT8UmCccfz5BFfM(MqUXC2A1CCCG#F=>u0a~@PxTCrSd^NU z{?<_x9PmGUrh2xc%3i-U`|_&~n=fvD_jnVaE|9fSh__gM0K`H~pF*vvCDv|_lc>AW z(!_TFWPpo>NFe9w(@0drTU+@Q9F0(J3v^o@I42$6xSdE(HzUhtbuE^){A5em!AV8h z5-ar9UiHOn!nbdY+q$htoiV0+DgJ)QK7FC*$MbGjJv^=aEb^c*o0A{G8UcMR$`zb6 zU8pX@5B>YcN7Vxd4v0B92-&L+<_umLo}-fP-jSq=fZfY$0j1P@({dCaQBITh_)2u* z^JRo+(1@1d?3|c*6?c~$$_npSQ&SVv<*qRPf}3G*^6KB#!%SP`6Qbl3eklypg@^ZE zS2>X_rCz)Rt7qPa)E?hq7b^q(s*zvc)SXVz`DIQ(?}T#gPl(PyixQ2_#{7YJk|N9g zW*&r#zl9+mL|B%9GsD9~`$%se{N#Wv4PE~|BzkH<>Wi|1^%UFW?RRmXGmDEoz*UBx zu|d`m7C@*mi4>LV0lph)sJr4IG^5BihAMl<+-?aT_yha>G3|<$XoKfDrk`hT78Wut zF3%+-j33XWJLJSdBHb1%hnpX@Pn(Mur3ur%n)}N3lp7377Dd$pf38XV>xw5&93cV0 zuYCW^`k1%3H=7g_gZfBtcDA8+RmGb(zc)t?%YD79JvTp3(N|+V@3N4EfPzI3>W<-h z)U?KCz0(x(@=(nvu!Zbog*)&Hcf^=KonRcW9h^GK>^tY*#|#iJjtR5Xr>CY4<37Pi zI1lmF;-*1r0QA?W0%gp=&F!E5jd)CP;I6Yf2CLZb8 zv2}cGGJPKNuIqr;)eP4Vx|buVRnC~91-KN5SFgjLaH}>iKR-pz?hUNc$uK4-?kBPd zgftYX3!o^LmLr&qDObM4_x157w|_e_cHSm?dBppKdP9bs)K2lfFTYA-J+D&WOFxLU zf%An{R;D)6?`)E*?p^%>WBSry z*)Cv9ckHl-pPAx&=+qZ^@t>GLQ!SZ!fTDA|7>=cY}2!Se|f^dU5bzh)oe=nK8Q@~hm`*~`o8&MVf%HBs2& zV8`}Ne9y#H1%UklPZ+9MWoWQ*%=#xxIC)0g*dZsYdak-F`Kz$~ z;e<>$^=3d?fqDQ|#3iUJb5VZo1NNRvw8V7NG`;?r{`XM2g&L~<>gCk zGH*@u%33a`uT9dr;cX$?R8&h(%&V*u>d&Psv5Hs zBw6syU|=c{8-p`~z%4>L;GQ;x7mf`Bw;ui;{ruhzNCt6F@|<>%h9M`tn6Y0v)&Ztg z*bw&A$v+>e*Wh?{E>jE$FC2-%px6*u(pM(D|lNwjZ z`v(Ri)ib{dgGqyN#yJyT`V-w)O@-v`-@+u%!Ovd_(;Gf6M)1tS+i?6c$kko0Vtzv3 zOW$fY=C*`gKoH{RL`Ci^VP8NZkblh^rtu8k87z?$5k34pI0}PL^d0mVM3DiWd8IGb zM3y^lvSu^BD3*!r7%nAoqP96z+u~D=52{Z5?iH|q@+Xj zD^Kt8?CxsvXwSs26g~a!*+8~8bA4$ivrdb1nMxbx_Iy%A4Y`PT_N->{6C)E@1%MC( z`Iy5w`VGv_2UZ|q1!jjTFT9rN0L=;h5zX%$Xv#!x$J(;Q?Sb=f*RE5-Z1Be-41i#{YNmk^`xFdp$QY?rnGOuE0S8S1 z%)wcQ6`vxM#xBjw0jag}&YesoHg@)|lY+hPGSx(;#VMA%QK2&~4A`ofPt21O&PYerSk%hrPwe<5_*wY;SLyIn*<;!bCcl(Nz~V z2_f`5J~e;-xsy|qC_KR*gU##`rYW*7(y=-qEMQr73JYG{zCn8W#d=E~JiB7-OdK@3 zAPNKu^j(FnWYTjd;TxjB!CH?+m|ek8ZYsyGfD$OP9^d0U zsJPfQ;bGQWMM2r)@j?xVsc|voFIey|t)6|Evs{G#O#9I@tz*KgJWz<)aF?NMVgNIf|hKLYmAUD)p>R}9KV6q_j?e^{8o}kn8@Fs7fvRB^4 zJbxS82Xh#@QOa{Sj&B>lSjOSNv!Y#LgD!|8cmyzo!G*mKhL)--WwwI10Ou1^Sas+~ zOgyrr@BTl;!qX{MS?J9npTi1t;{b8TS}ngf|7_70gsTw!7{kK@?g3D8fIPyd4!goK z9a4WDQP7uLz3uG{%na9Z7nWi7gH)bLP`T{cvpM*o7;o>p4Qw9fC>eJFvdy5p5~|&K zPo5A{xa<5%9=3-MQ<_|0Uikv8a19^}5~pV+2-_l2D?4Et6V_nqGEo}<(AO(Y%n0zWj-u8+by>2+az`$_Y?!>lL!TqG9 zj*bEv#rq{i2n-OslKe3G$Whp6>4&w9G-DhekWbV&lSnElKXBJ))NjLdHiW~L{~k6H z$^0XP`tSiEwDtzfq~pQ9b@{7gR@K+Z!~u%On941`+&r(kJQV>r-Zm>U6YFlLg>8D~ z}85`3`q(>SPbnjV1Joy(~K9Ljk6JIOuSyq6%0fdkQS<42P(|ivFChunh@#sm9 z{SbCO^lLz35&$PE61}NCx0EprX%U|jx3KQy#WFkx%s1$$p-IJnXNtUH`&7RMBtAsK z`ME^yek@ObqJg%7&>UwE6vynmQ+p>VvPh^)O{<-N`ylzji@I;;&L=JB8xq2+UNBz? z18amtg5YYCuseyQ3dEU1esw8mbQB)~;uE+A7bwp_xcdV}Gaf2TkJt;J!T^D3jU!nz z@OvvCh^s#uo0h$k^L6ct{bb0f^kOU>X`2JPU!tS#;;lo%{Bc z*H`ad`b^i}vW=CBB!CFN-zFsqYaNAy0odBf6qRZWQCOXJ^Dv$GV4OXdbwMOr(+u!N z=%PVua>6N~UTK=WKYHZd2%)yG2ZHN7gGsCdLc$7M9L8pB)OSD8%6VtQaQpp7;kxdY z<(ovZ+~q0EPf(YrNLZ~!rfUE6U5ElDWf~ShA&_g_lV7C3sVMhymhPjo^U{JMD{$I{Y)2DEMIKe`Ra3)jyTTT{{jH|=})EL1i^rd~8C)_W6*hZ9i zeGQ55!W#Y{vL{d(D z1yqp^i0uqu`-PIlz~DZx{<*mhE*z-8Vu6>AedR^>yx$QS{tV)1)Ww1B5&B^;78+i* zQlLOs?d#Q4s2p!=Kf3(k%J!=w<)-bvOpYJmt&PU&33E@5zS8*@6l~+i^UiXb<#32& zcgQm;OviULYPwt{7uyoxnmVv;+mSTgs-JI;$(_gtyAJEP$CGy=l;q~5Mf60XAJxq$ zV)O#ZUCZ0rE1L&M&COs4w9SZV8f(`l-NOw8A}_MVQ8Q?PY! z*#DbOT)qvq=$jbbh!Zfa(AMHk3s>q+$H6jJ1db#>oc+KT6vA-`w`#D&VbfKG0AaH$ zV;cj6NhGMyVF_vq+CXr^_TFc@%aT>Y>{Cb;&66X@W3HEN8J&Fs>kwu;jxSs3&%<_! zTG*J|`j^Ezsw-Ry=V6{M1o%lW#{(O%+=<^4-8S&{!=MQ^49O3fPtrlQOX1`ZW!N3D zB!XyXaoKmF-{w{#x7r@8*%1m?=t;wWasDveaSta@E{0(2e->ag;S7q2n5CDKP*tKk zrgPK^_N54JsXrLn$G3IorAV3IHgA{dcy@8jL^LJOQBPK6`-3XKs*>xGF zBUmwCVB}crJb**hVC5U z&7l~pPMr^hHb)v~-z&s@*tU1CAp-XV`G}!;T)Z#*TZq;Z;fQZF3-&)p{~Cls({O7Jxq~8or{!&G#fHDhCCaRU5zU-Sds0Im zE?k;sEZJqLPZ+tIclpr@kVuNs2YgL^9u(cV6P-Bnsr*H8+4PiyNh$1BBt@JsC@w&} zX<^h^KfP`-U3A9@QWqiliM7;OQ?aDYUMg~O>kAo}hL6$}T4u^PV=IU|h`7?i%Ho*C zU_#~uX&WrZD6_tOtC|&kTw7bocUf_esBz9L++g7me`}?FrL$`I!&@wkl+_e&jy9~v zS>0fZ(KgS*7Tz?PnQ(P(ooR9TAsTA`A?M#d)iX73?SJ9%8oN0-rKoO>hKaIfzI{J% zgNPb_OC^6Rm@0Ued&2$EP{o;FzkXdTik}|s{eZo_?37{0n>l~4B%-53PByjW!kw5H zpf_s~VxkWg6$!T(eu4FOec}PcNR_pc!zOnq|gizu-*b4Z}6oK zCYWK;NjwW{Z%;2H4fk?1kZSzmTe6@9^VxNk6tjFCEv*gT->u_m0x9$TA zcu$E#89nyvsr|cbX?({@x4uSz10yPyM~Yh-xgpiRQ+w7fp$*EDn2_LL$^ist!W5(( zD5s*Dzs`k50(BuIIU6&m(93ev>ipdUkvDyunreZS5L0S=cbu$;5B$1{w=1?DDOE6i zD#h$4zwwo=l5R%@OEaO+Sjrj0*>;^VRQ^1ZSJZTJZvMGjdz&LteiR$ClPqL;Rsn3| zyVZ2I=;Exm@|tDnbIsvro!(DVx24R(UkJR>b=X8_hKKmq{jFMW&HU!jvqNV~ce2noXjr2_EqnujMibRD4xMuWE|RgfmaXmM zHpbplfZQOO^M+d)X0Yj@%Gjjv(%}4aYj>W1_C!rP`Rey_95b*`&PK=(HP{z7eHtC* z|4nASsh(zWfnmy_qI@uL3s2yixsM9q9IYc6XB<7)d_O|k@UhV~2ETXbEIE~27sZPJ z+=hyt|G*jSGf4ZbVVy+`3_3lX;2Qd){HU0Gih?@wv~|g|YK2tPfxA#4-0Ne(=uSb8 zq6`Q;8&Qibb>vT)i(LX%+5t>L;;RQJR+ASwWWpGD@wl81t)6i!}#cYur) z6@kY}`~DvVw{Gt>K7Kovo@r`I&i*ZtjYnl?TvSwKlEX!_k_jZ*4B`n8n=+83bAHa> z{=3sJ(R*&40Edu}Dr{rm1P;L_51bfMY2I*#2YNb8&-8TO=egB{re4z&B*?FFtDo0a zHrC%;%l%WE$`fMQhx0zO;eCgcit4TF)11EE_08N0;*Pk20&2msLzz)~k>H-#(&W^Ki(a$7k*3M<0G#=X4veXVh4f7KOt%rBD*Dt!xI zsf{BW-BOIMX$zOVd2BfGE-&$9)?av``^%%yTMiB3vQK4@NCgqH`;EAr>+bukt?k@n zL$ssU5SCYEe%(tf37I1_9`N$`JNdn`EHYQSW6}Km%j?PhqU|A;*xH?BMFutI zNCmGRQx+N;wJPwYU37rW149O%{5d>t&`m!d0Li-AzUOh>l|CON@pA^93kQ2!iqBv0 zW@07Xo;qo!SN+*#b)@YuktmdNmBz~`328y3Nic2!d1w+o+1Bd|liqQhir{nSH@r-J zmygHQs`(@NXyNQUZ`sZ|t5x^Hy7_{uPtB_TfNgp*QQ?-6lI}cq` zA+Q-e_bpBNtSTYixVb$wCzHZasP-wTA55GMz0XC{8KC#NB6{0_HP7+xZ%=%ih>vk! z1Y|DzLZ!IDT&{>xVnAb0Vgq26$UI~CFOU|-7+|LAgW^=Al?@ji>mRcPy)7$BX@!<_yH-GDSdr^Dc8y&RK`et$SLLYd_yJa z%aP7lnk@Mp-_6|GwGy2Zu-y z6_b9LtJNSiIbg+b;gU?>s^-uZdvmfh1HvBSDxB0vcC2(BVlCu+;>m}$I3x?qDFB3U z&PRSP^wk(R9eC{e?vE}afN|lgLoNmBd)S5QhQFd9VwbS6bZrLS9hS(^wtMSiU#*X* zC4Rp`R7WCV;W-+ECRh}*9ZKPVq+MCe>z-KdtzGRg-9`11I41XFD@8icj=1wmwOLm8 z6-2LqtAfb71}o?!CO$+Z>*ByrOGXwf#F9eZz)bs%wQRX|?Qd;ee5v&-?!~_iSpn() z*orC}xeo7Qnn&83^+Rg&XnA?!96`@adkC8f>`VJuKO?!KzvGOX+irpBCPMZ&+Gi|p z9B%#5b^!dgKMwAjf>&Y_cJ9Q4Z)<6>-2UVT&T1g`QJ@E3oZBXUmAbADKFhUrz*U1R z+K5AN=A%c~ff{njo|l@tGnJM0F3ohC?fiX&J2K`q*N}do*zX-W&U{dJE*AFta%>h! zTR#0ae9HQ(gdji31=&ed2Ky3ssj8-APTbe;z$z@vMvrm&+DmZ5%zLNXBdhyx*j-oU z%oB1JaRGucCs`ioOyD;}^oL?RwGC;*3@17$MKsOCmrVqdO$c9eNl+q@F=g z40MY@jy@Lt>o~BXID*`RMKv9%03>1%CY~|sG4siNMft?VB)8|!t}9);i5?tC(EwdU;h_=Bqe3%v2sl}IWb25U(>IP}08cMVN_E|(W|qEBri&*) zn2X`Cz5O@j$^k`~UK87E!$GpZ#{vkhz&H;B!3Pl+%9Db^-ALQ@_q8J7fo=jgvk>c4 zk?`JueFA_%e<5TD5kLsZFD24?UB`pCDAa(kq_B1s#noZu2_*M#-MSTgfqtckD*?6h zduQG#7TsgYl^Cm2LilljbZHh4g72biXBqgIHt5Yipc z#bIYCbsFC%#(q*@8pJ^C?VAheG?jp4 z6zM^M;~OOHxFdqEXX7AH+Z-7dW>-MtJU+jNf^8Y~Bl!Vr2ZRqMl0lxayafmh>u3v_ z6|e|Z&7bsgfs61ua{Vjr-UFqCliZ2DzbpEYT*jcm%>lCmu%?_YTUw0jOCOXK&z^MW zRkVLA?ticVQvj6z$7qy_LKtEzWY;Z2cP@PEnMBR=f>2;n`KDpRdBRwIG6243G#~wi zfeWPBvDG^*>w)nf?3}aPY=+LA58t4bPe(%A>Bq!lGpjh3PBSI6F2Vv&yEWlP*#@rg zB(x^6cM!_Yp(v1w8EBK$M!N@JIMPw@F5hzVQOZj<$o+xDD$c$^rEY!)h2O732nsQz z%jzML-@vpq(#80Ul+L3I`yS_QJ}<{d0o9;t_W8>f6jeQeOhHQXoF;MGGEwD$6wa3*>1cG%(P3$46GT}7_gAz zquffMK3&QOu_gUMnJHwQqDfSHX9YbwgN(+}+LvR-J0xR`cTPWbXvH}++4qlK?5nNZ z5tIfbecgOr_vwRU)6?(Id51iHY^Y={^me#G_Vp4mKMr=I%g?W;s?G9s#lPdsK+zCD z9+GME9^$plk=QaRBqIv44@*i$Mh42H=u?Y@6kVYh=p;n=EJ}=#kEg)55lh1qS#GL= z;woBN#z!Xu7TXT+pw^$saDo-=|W=w1EGz#_}L9FHazIMTtEzMRebiy8IQ9 zxWzh*jqUI~?GnfSXSgkUjY2i9>mFxmAY$b5lvRW z@OqGva;e1FQ)vjrJ;(rD6}#6_>lqk$jWn!Rl;e7u(FY1!<@X8)h#PRhc1~))d>8-r zTlW8z`O*B|ida<-Pe*qdl1P40R^&dy^A?GsC{W+z*t&cEN1PnMpff@?7QBNmt>VEejN#VFRJmf zPBzrITIiyILSy#C#0mKwcpNxeQb3y<4|Fq8#e}`~ttl$|l9$$(H51dF%Czof*k_zNJo2 zU#9m?VmYIzE#OVQbz~0!>lhWdu`F**0}mFP*e0kDxF?u45Eba7U8*2r;>8)9>xPfS zCOC44!IR}jP8*wZ%v<0T*qhLH=i4{IZ7pTG{K2-Ty_Jj93(w_!p!dsv`n~J@dtNyy zmXcFR3<;TNuP9>%IP=Hxd96opD0xVr*g-fzeOFAZSZ`bstG4YP+XU#3#0dzlb{^4M zHT_Rau=90C2U_4zo4yiJP*9MP%0n(2?ZPk&S;H##@7!M{e(}^o&y>;b(&>B_B#jxK zy!|pZ27xsOJ2Q{0@SZ)Kw=QW2J97orQ(8%U#BlV=2$2(wq?hDrh}-@aw`IUeH8-1GW-AibZa$B~>t(bzSOZt6RB_D<8s!_|{AjW1^C>k+*vW|jJ1=-p3+Aq+yDn@Ldz)AFv zL`CtB{QD;xc9C>m98i;ear94=x)T~l>k|eBl;&ZS1a?RARxJuL1CmDM1orGH!oer4 zl*QjRi-#!$M-miM@PhMIpcyB8SA@WLYH|vtXBrM_N-Koi*CtNyc&;hdEUs;>pi^LU zq6vyY{y}Zu9cSG#FI7eR40pUrzL*DsBzH3D6Q;J<1h_9@K5zjAjl8|I0GBW zVb?=1rf+tPS0eLqNWug~u4>kxyhwNE;ewPpiv8m@)C|56IrxBENjDW2Bod z@;A>S9wnd^H8`lQdmZj$8b&mC&CNx33nN$hHTVkP*K3XdNd-d8RLwtDSmJbgSVSeOK(VsRIMtw34|Ut?kdLiUR)?d3WQu9Ybic34+2 z$x&iLA`N;h958r!30|iy>?;rlSICNXt&exCHT7ltb|i7yta3#sfk+-6{ht#5#0lm~3WnnB*@^ijilp};q2|KyC!VFZxxQ}CO)cJ{?2BDLD<;xP zA3Z`l9b@BGg9M;m1X$=IM{x?qLke~(fBf{ZmD6rk=FRFJR*)dyjM}tuBUqkeIEaX8 zp~ngqutI-IQzqT=&5-`GyuC%YHD;7rjf%|1a@4%?x*sIn14#j2IvhiXy@`7obBUBc zZUL`KysJkbbvAI|)(cBwdOpDHxrOVtV{ivF%rw_jRN)rL?NKm*VSRUT(-yWH3Uy;{ z=y;0G0+=62d7_J_fk4AMb&{W^*!cs8py%cag-D+vShTo_E@ES}WTa-)RCPt{*MfTI zgv?()2ku51<`ZU8J9nZ3{IER9Z2j_%nhMj+4iP6tA6kd+*)tZe=+m|3y(HQnZ~n$~ zCcW+AIubgvVG_Q0p^?oBA#|CkbM~$EB*@zXoA-n@?mIvGV|V{9!H$F799esYCZM*HADY zlZ6dx;H%RQ7Vk|Y5>MT%E05qhMUt+cVauJ3uNs&~X4@C_PudGvraYasWfDuzD>Tl7 zdPJAEL0c0?$D4XkA7e2u-Fp?A;*pFECDkVaKYx-^XAE8v?Xrvh`b5)%yP27Z>{(g4 zKdIXEsoe8QeyygA@-rp`XGljr8~t2R!!`Vvt~WdR0oPX7av%JdKqPz(mqOHj&}tmg zj0uur!iRzthlv)@UqeGWRaCqZGA*?Ogn@WDl5y&v;Re<}uJD2a*>Y156DfA4hK3+@ zOIwQV0lu0KIAupP>H!vDZhlE2(Ne^kk$3|CfC)Vn<^sjS!^Hp#4{S0FhrPYa)YJT= zLZ>aA-`p;cv!A%qwKQaS**)H?%ZObA>CL{og(rw+Qe-T7hR2WUAeAm{&{D&VW9;$! z;!mAR2adtp+Z)I|<2>i>PKq~J7*2HK@$~dsTvf)P^{F=N%+|Sj{5WwO>R`HW<$j;f z%hu2Ci2lkN{S`1kGvTs^5pSN5A70xlhK2^eKXtxr_#5ah4!WF$ ziHOUrUUom(LQv;PcYpW9)U?OKwicSsNV?fA?^Wea^!D`B+uNS|_i}QQTBe`OLFmAB zxNu@a!0lIKx=QNAu8Anfk;J($wzeIz7V=+Y{AUO=VyBlEsXT@Iv^yRQ2=d5`s`j=! z==%vjm~bT7SEKFdt(Hxnbxu6^)hp=^@n5J|ow*X7y_$Ef0!?5*nePIaRbfroOb<7s!Wbvr&;bNRxyEize zo^?+MYTiRna~SZb$k+`u!8E5{jvG=R+{5&J_C=X#&P>Pj_mkb(SLnJjZ;RO#8a*)} zBnQ^o{#*vtquyf1d(|LNgkJp%of;eS!XhU$xa0GOJOk-p-1=lLE-V<;JndzYm|jyO zNIP`a;;cnw@L#eQ3+k83g5L_9iSJ37-V&v=PKL2uP=4+$Enxbega08WWA3r5`TWrI} zp{c~7+Cx{--}-G(-6fs~B4q)y3{*{t>s&DrPIQsIY#LN*ciA*Hq?oI-`3U6G`dRWk z?d2c&b;PHdT~Oz933E~}f4yu{?X#C7oyy5n&5}#Y+5-6vvpZCTR%OOrG)V;Mu!QFB zNU>4nWW|`9TU*3pZXTHqy}{z&JrN|e9ey$x@>U~VF+Vy^CN1s}8PMl_p0WPOo)MtDoKW882_q*X3$lI15YR z6z$aeI$yc?<4^eT-yJv6_7$DOB6Ck$aSq(>A?IqI+onugApot2?vSyz=7==3h~wWj zvm~dI(mL6rd-u)cnWby?;d(yR_9;4tPicoUT!>@Q-SXKjkK#k7_>W5z&6>yFjd)8% z88(k4xD3{f^1@(Ds*^42AB`lccM~t7IB5pOUWH+8Kgq;tAEKEa;qn;U%A6SrY>fn2In%_7iaVr%zhoa?PDUU>Y;i%%}AkNMsd$E82y+8VfK_ost7L_}P>RhH+(F<#m%JCPHN zJRv%#&XdY!4mMq?+JAy`^>l-aB3H;tTbpb7I|qIR(lyhkf#g{X1+u9?sF4t6oDK>F^LM7$>_~}2_`11D-PTrdFsr0V4e|r(x4H~eb#@s** z$ljiGGHb1LU^+y`Fr|ZT43+QD(1Ug(E{dscWlyh$#&CSoB9XZ6paYp02Sr{Tt^Ekv zxgKs3h$Vgf)))en%-v)sf#ww(ra7T;363zn-_w>dd~w_i=dy{6r-`qpz4m@R$23c{ zsqLbDZn=%@l>N2OO17il3BS*p|0=D*fhzI8(efo@wXx`x;Z_bS*u`fDg@@;t96d;6 zSxnP)bh-@c@-JJ}eN-gAl#nwa z8@>nWUw-VjN(@ks)oS}*-|<~yVN2A=2D_1K5DC_F?ASzOu#2Zq3_s3WAscK0= z6%y~R7n-4a?Y^Xw!{eRkZ-cRIoHrx+^*1Uw9mIIiz_jdH_rg;=g#io#3#M%5heV?n zpZ`feCAgP4CSo(IV8`pRceCCXrF-ApRW5QZH(&|<>^4nWEE~+sXsi#&3}y;EeK*L` zCE@;GJ41t-R&nib^AgupwjP#K4kA7NtYC zQ{A#+C9e7|xM5}8yxC*_u({L)N%z#~*B5I865`Jc>E&>dWlLFl4_55$44OK1V<6u8 z-2Lf0KL+#)wl;Bc+M6WQHI!mE-}kUq#?zmeWfuQ5(VF3>&k zkwKC;R|ws-IYz^@#5n2r>9$8}wS2d_r{kHiz4uQshxVsor6a~i zq#vJsIRDLfgvA&!IOorO#b<@+`7iN#xLbG<7cWrDiO|}5|NOCsnor*Dnc6OmEV`D) zNILS2M0gbU42&|<_i6JiZ3pS8T_pu#1=xEKTmpaC&X#rPA)EP&PKUZ zSK6rA7Y{4+wkK}iUd9>o@*x&7fx7Zl-uz~{?qZ1ThRzLLTG3D1pm09ez)ih)YxbTU z1kRl(^=RxLOP~2PIvd4W!g4)#IWJYba8lhhdq>q{KXHp+mj0=AvnSi%5q*}_ z@tsTD^Dqq_z#IJE|F;n~jN32cY!fS z!50L*)Z5}g)1il#MLD+Y^Wz)|!}3knN2_f7*EVLqg#U429=UoBT+07@pCngPw`*Fj zW0;OMeS6b$?aP-~28&pwpbISX>zAZouYa0%v8%W1a=0C#9k4%bE9ZStwi^SSVw>2p zisMS&vk%-IFwl6y;&vQ?K(U{a%P5v*X@b5%IqZWE06qHd(We zHH>PFT<}_m+sK$RqsBHe;21@J9PNpC&BS7Ja$xSES8)7aHEDRe*2TJjvLJy zNwg&0z}_2ngsY5Ae%O~V7YRmO4ZX(8|Nl{(aXH_9KD{hXTw07_WNo)EZ)2J)ZTFvI z>DSg{#(Or$yED6YpPSGWz^(J5kNNMy)aqJGFf010LW4Cdx7Nnc%0-P#R8?r~cFp#* z6^-W^xz#80aV_an<@fL3WejVGL2}QQw;@|F3W|iwamy&T)Rk;h{*F4`FX?vX19P)> z89LGouW#{orLH8!r@p2f@mynJ+HTutc~wY$RL8xH7Gl0`l?(N)`Lh%{DcD5f{`MDd zgSYzc?bzwV51kn~vnmy8NMk4vw~i#7VRb4w8_H|xX5t)n00s{=1*GGj3HrH8q;LKVwzI7_@HEOzA)R{(}8JyOxz+ z`umzF`wV|&cD9+wBYt7y*FO>;_PQVlafD_9t=In815vt~ql)XEmzxT6pC*xV?s72LrQK$akO&Ln zrnim0K^TZ<7sv`N=j)xav{Z?{;S5NKFk6yfh$T+Fjpvb9!7G#>-y`lZm988*8T0Vf z*t32|F)g&m39$qL^s3^&$a&@E`22cms=x3peNt>6odwjzXy-P1YG+I+oRWa#Y-s3P zb6M9st58j7?2$_j&kQiHCrGq5GcK3BMdQ7*R@z&>v|u_oe|Hu&=2AcHaHYiLo=L6V z_4SGt0aqFvM;-HY_|>nnJ>Q2M(HtMrQl15wMU&SRN9C-R<3AjHx*xdJ9}`;yXR`RCgOS+lB$2j=H)^}ZcGbH@A3MzeFb z3=KzV*M0X`m7hv(zR=Ksdy`^xoH4lg_z^~$;)4f|iQU!I7*(uuH5Ol_P=C>93K?h%#iC?08@*kEc)=Q$_NJt z|4Zp0r;~P2__p>V8nrrRl@u9{uzz?0^6FeTg;wWcm*}yVOF*p{s8j$e*xA#%Y(11VKb9@wV0oRk{pZ%@7%ag- z;u0d-;Y*D!R}Ka1L~Cwqiguk%UZlCKUbS7Nfs_47j;(VNwOT#e4gA>n@s)VLRrhCJ z&pi+C%Dp`z;wg;d+9-*NM)8N>&sdw%tk|Xl>?k&e~ z)R=Y1NN@Y;qn$F|Mx3Ws1AEHIc_Y)NuwQ++arT$fvpn zB_sd_o;+c41~_psPg4xXt=03#DVYFD#;NX|izj zgVOtl55;28Uj$f;59UAW6d)?7^E)~Ji26n+cTR4&zN@y+54sSSD`fB8Djzt7jyBbg zFi!+fUSl;lwuJ_ zhi2GG4iGBbZ~HAwr;Kg+B*g2Ap51sL;A+4fopi#*L1QDsQBC@dd_Iy6>*)rrU7vpX zG-(t4K!8|RID_Tk;Ox|c`g#GIHim^qxaJwqlj>Xz^0sAUw7k@G@Q~wWR|{`+q2&nh z5l(EopOVsXuJ22`7-Wzn5*~%HupgtoY0cd4-@l33+31!E^DR^$XM-ei_H4^~3l&=T z?igfa@(cjwfosMk_5**1*Mi&S?+QEX7&Q^bmnoW-&FvX9ARjbI4cGTv2?_G?_pKdR zO&KDjIqxy=W;NEYv@Le^*nTM{$Y*;(CS~NKdJQ$i#!Z_dG}6H||A+7niX-B9F8ydX zS$onui3$(O{V1Gxp&e+9*viTZEJpk6-)5t-=X%T}&6%p{@ObwGP482!H*dryCSs-> z3)@0wM0V_G<>S=~2|hKnmG{H`7>$T_7x5RkikoGpzJFxhxAA5~ds;7Jd(xeAWx~~7C-n4#GQ@Rr zUQ(^w>FRnvTU;;aq0b0M+q2!LycAqVLlh63ONbUJ?PrsbQ4*=&?UE|7tBWRM3ymjn zo$D~lI@734LB#s%ZpSjgdO@nnZ$!@E&;VNlDjHaYio=fN;+9 zaAPXN%MFPQEGP?cLn^edJH>cEoPBurSctY>PO-$P(n_=avKe2vp5Gn%Ug>`00guCL z$CW9GU#DN(?CT#5Abe_2v4SjR8BR6Gqfw5IhqxVGY^kX$oXdxRo}t)@@sB71qeCT{ z14D39nJ-7}xtnLxepa$y;@(D^_Lm(GqHU~h?7Dd?qKHYrHZ1D-i)It6XUPggAGZR3 zOti>Jo!HE*V^2+^6OP+z-jLeW6&GhKAontW7yO9j=>aJr)mP@Xk!A6LA(?mLM!OQX^u z2%+v36_ed}ky9i^D<=;zfTE?luTzTr%UT4SPWdmQ|>aSn?phW;flymU_(Bq?0 zQqZ1vNcjbo%m4g${q)y{2w(_kTfHmzRx%}u9!FIFesxJy^yJT62Pu=hL}2236-xKb zh}@IaZM^!6oK^eJ&>2UHT-OCH3oMd zcnQIknFoiT^2ej$@A{TN;Dg4^!02ARJ;J z0&pqN*+yVw0e$xlL^sMO@`y3~E^63>%q9@NfX@!3l(b+=hnyNv1KoF58klLW9`mu8 zBDpxPo1AMc8XorjW9RUFSblT+|7!`}2i`Fzu;l->Ztr9~Jlyi^!(tFojb0-MY3TT1 zHen14e;6fVC_boH+9x4pp#TLd@HQWhMMX7x;io(qT&Z!zJrxb@?AZ-Ur1S z_zl4nWK}*6o`KGAngfs}LX`5WB_8YGLP^!{)m4V1dM09=C{&FEy$QZ$3PD_Kw1L}S zmR?J-J;Tw~AYI}Qa^F4IMHUDb%zm3mA~A#g^b|wd*REaj0F55d1FrJ`e4M*}pb0_D zBJ-wUTD}CQ0xSfeetNx&9^RaP`oZF-v?OI{tXpcV<}nEbqqvUjz8@Kq-WcgTt-;nFCVs#+g=0;K$8? zR1r=@W_?H!m{89`*N$=TQ-pfPUXp*+wOGQ-X=FA{uIQ{Np`jy zbK--Emrn+NVz@p3AhDM*k50321Z;<=;3|Oyl`U=+)Qez-)QV(n`@S)@u@8@QYqDw! zBx|sEz~T*y#}4dzCF%>S1H13{$&ihwkt>Qlir1ZmJ)SxfVSMHJQSv_UJ+p+$w=}VU z+6)lOfY`R7zWy|TLN5`B6yLaR_m!=K`UrPI-oZ9vK z>-H{=*~`6yeMvb7e=<^q^Yr|)24)bK>B=4^p=3&-krE@RN-c#4?uZh}qREkKBh}qb z&3XFu$#7|OSXi~=G8|JM*w43t62HB(vuX=B6Gu&18J(XC>M&6EK7Ggg>*I$s%Z3Z5 zNL&5E#t^CYk6a%+bt0zUPBl|K9E=t0=dpQj&pefcd%qp@`Xj~qK+uPS%S4gR-wMy+ zp`il(rt`Kp$W?UaDoBdvyl`&$+LspgzSxCeLFbLXZ@Ut6v$UEwD;%B?6V`)*b$GpfKNv| zYi=`xWWFerL9x+Rt+x}0fik%%hENON{GDlICV3{F&+Yx>kUR(55NQ8Cw$5q5QV6C$ zQ(jH82r4%eK`UCl1^^W3x{Iuc5DpTu^kSjj@_tuhVA4tE4eFDTsw3|p|wh&wGH zqwo->d1re&tdwRvcOGDeg%W=?ZxcCe-kKGw>WZNmDfp;*d-nT}qIR3djYl18leP_6 z*@bJP2|)p0d@GcScd}u9Iq|Z7Y!l~wrQ{OMyq!H#Enk@aKu(lBg~0YNBxs$2s}1C5 zpwT{o32$d_zp~3HK^$S22rLjUlZz)$@F|G&4mD;v>n${b^Iki*c_}z-6s)h`AFrXc zC+g1Hjz65sooiKS%`Q+baU&c8r2b9^jV&!Wgh6mvLSqP=1vqPfMf?D4MsFmM8)n2G zMt$tJ#bRTF)7-h*jK2KzDEHVk&n^$btbCHq_|xNVMjr0P@9gL{$!E8k>gYCDIiuk?a8~$Tf!p4h1GYb%7=)0)uLc@P0lSS=<0G2UWgT0kpD;T#G3?&f;w?gJjKpH*-g0HLwF z`wtoc@)P8+xt~xsbvmLzm*#ECdhLsF=VRY1IP8-0OC*i4;xUOnmsFPyC(9oB-jwW} zDJtdfx-S?iFz)a}aQKf7)u=mF@(^SDBdhU(aYGj@WV3j-$o!FZ$r4i$-I%s)Q@F(W z%;w__wZ;9j@N%~pS{sR%%no(mRTjo(It2SJ#zUw|-;&*HWHe-+YPl~W-b49LEwAB5 zL(7In-BpVB0R?c@mOPd_J{U#P!Q}v-n+Q{y*bb;g;3DIH`0y0O%-f)-*|$px6id9M zjC|o=soeR#lVF;#m#Ehpe4=?wc$b*wC1v6J*nWBjEdDK!HKCUoa0SCsW^WdmgMS}1 zvcOiEfp-&5Xb;>u8t}tH50<~*Tq!fn%$BB_aW4*In0?xAvyg0C-+0(^i^EJ_?C_0R z8jDGQk#w)wz0KZad|k@s_e89R2)n&~jGQ<_y^}%`Hdk+9UPt;Dfi$6LLFP*gvopSR zG3h6@cSpqV_7=gX?y{MNG){V|H7)uxkCI{wm@(DZZ&8Y}nCnmI96v$K0R8$^5 ze6W8!4G$kue8JC$Hm0z=D~StAfo!p-AjSqqJXCa?_ch!|DJizdTD`IHRf%xGHrcFM z$XlScmK`Zl3jnrFQLjQZ&@{`V&|?N6%H~O6B0B~n9t1+JZh`;Z;s*-^I>*|7mWuf{ z7NJ32pn3JxX3Bbk{@?lrLyH@E%kJN{8X7j;X*#6{47{-kTKr??76=w3-KF#&UbI6J z{2hnIC$}QX$^K-FN`u4*jJ4p=I71`CHn}$?bF#h@*mo#w_rd3m7wid3k}9|z5*AX~ zwqLnVXF!F_+OCP;swSzpXHb|hZROCqsKi#WX9l`e!xn!a$P@w9;Ogal1cK|qlWcSE z>gT_29nnA!c#rPC`u!Gk%H^HkwWn-#)*IZlh-j5e%&xj0Sf-4r*~2vvCd_FA@#O7#sWtF?}k7iX-J=4!i zzO~ncESJf1vpBkMaC8{?_4-9qM|V(n*8bRZYw%jle@4NnHDp?T!yBjJ-p=kWnt%a5 zv5#=PfID-p-_M+E<=b(Q%ga}4YFusTqY`fRTQ*bkkUx92s$sRh7>IOv7I?OF*^M1s zS}ps#ecBMxklY{;6f=juo>(XTHa@V}e+L?E0ZIahRNhMQsi~_Pi>!iqum&g1Gw>Ba z_VPWmep<6gY)ieZmj+cYlb6G&_Sl{a9T)>j68!MnaMrEXa)w7Ub%yp}fPD(A<^cLo zwK*F`prQw_4=&2}cV-YEf2>L?G;$pVv!uW>1onx;qviA(_adE|zl$-7L@;t-qyYxO zU25BeU+54=IM)J!<`6C+TT?YxQ3J*LO<;_I(M@n~sj&D_%?;2&Fbe=QMZ|f145WnU z^LQeUA%xz-rT-FY9f4B}AO_)X0Tu{FYT~7SVii@@UD!6j7W4^FcR+5S1SkS%r~$s^ zp`X6}Wmd4<6y{*{+k16k_()xr#F$}DKU-a_&41VHMA$)Yx&Q`@^b?eX=sIA zi!3+Yn)cLl)IB#Ezp07V6&g$i6yUq_yN|{frxqsDy$lc3BwZ!v^Iy7~{*6WQX<~6w zQ>$^%(+sP>{(wOR{psh&m5_c1mhy1g`wk%g2fr2k`~3A{0^*R<4C!gK#kh) zx&$0a!7W88?Dh(h?7?N9keKKSb*O=5V_#ZA z^6guvS^rSg3HwFU#lZzRhX#790p84?Wm@!uf%hCZbZ?WAAk`1*zmTrC2YY=Jc-~On z@S;vvBlZ$EEDBc-z_X4*L0#bzDg`bX0Oyj6=RjY4A&@vJC8hq=ib_x`1`3E|=(KF` zYkI=1OgUTH6KFz!a|QT`GMnva=wzw!V;vn@7w}G=AAl$Je~eNX3z!sp8p} zDQpV{Br#RF45n6gsLL z7E}O{(rwSbG-(TivRG?TP}9!wW@Yo}(kEnzD4gm6tI_xF**MYHr;?dN8m(hazt;Ab zH=1IdRz94jkHi&gYgF-kiEidk^U2r@z~%@a;4rWavGrO%*-p&6qZlCUG_b*-uX6@A zZdxEoP0suVRuEui(C-0I{or{*d7p(Gr+R21m-%BY)?113m_MqoWi?cpa#`) zWl_;42=U>50^t$RVi`B4GoQnqSxnnqXE2cpyBNt(hj#hy{4j?IGDqs}Lx-+nL?N!*LgI5oiKU zIV;Kl?6P24cc}3Irw2hR+G4@N0?0Gd?rW$=Ab)1Rz8G}fOQXUZU=9Fxf`oJ4cW6co zge(EjT^Bi5)$TkKeqqu~uC4S<{DbD zjjs33yuPzDU%GBg6DrWCy7ztec*RmSysTlD0Kr;~(rA7_f`ggJ9?fox)fh#YTK&-E z2udZxd&3(l-HC>YV2FS1<_4jU1auE#o3wV(S8iAe;nO(OJY;3HINqI<>eq5J2YV;P z$}^)fppFW4M6|5}U@}R~)WYtP_8YJ}-{|JGL=Vz;SOc#E#$Vjb=r=3D7` zlGRV#-*RoT4p#89;yXM$%aKrWvCm&tbF7x`;}cJhPZ!}7CH(tkJgfZKTh^BaBaLtZ zK{W$>s>aA*>dUQvUmv#bfH^UwZm?Sdg%Usn0JNsR{yxW8xBRgy>QTANXKeMw+@hkV zEHTr2T}sPqIrhpo3=2kxYKo3vHEa6Y%?{4oBk;cg#jDJy9WV7M4(f@5!ruPA*K7ia z>9J7$fq@|TMWLSjb^c*zYisMv0F8%g{K=`wX`$UT?cj>PVw-1D4}9BwyG#(GTSO?| zl&5TQmr%Vg9G9E;AO5}{ut40o{-u1&YcVS3ua#U-dtXrm~DtcXaxqN+&M*R8PcXR>w;b<1<6t9P=t1GkZf zN1b_5+5T52C&u$@YmWiwQ}pp8qFHA0<`DyY0BjW0;JnE#$146p16AyNBqTS^cslj5 z>ouw5hH}pIh{=eb8+gfk^bLGmY;m!J!Ep(pkgEJ~Zm(f&rs)D;UZ26jgT`G#9l+kv zF*Gs~2LA7eolO9EAe0wia}W^`nJvynDKd~?UA_$d|Jq9^Sc`!eKe zW&vjfS5vdC{`G-NBd*xAGO6A*1AO!SE0uNVuM?PXu(OPHW&7eZ^sDRWST`D`872=F zXX=+LW{^$Kb9zPZ?xs6(1s} zZ*`L+zvNEb2Iw1|^@%51J2w|(+>JPNIx)lml+kM3nd*{-lG4D!_^R|{K!z};gQi1{ zQf4BqqW0@&u4j-csQ7SO65cg|mlOV3fIU>NIjI>Y0eXRpmmE9s^v8eWcL*OpYg}mZ zFGl{SPtdp(&ou7|eMI*mp`%^IvjE<|2#Fw_bhl5rbo%jGd2mG$0RRka)k(l;@x{hR z8-d{*+HFz>%X>=h3f#CL1RFdzaKeMOQc_*5a5h89VQ)`CMn<#J zlB^2t4CyxE1{&Jx=oNe4OUdVXh z%WS0(@!L!Nlb5I70=7SaLS@Cf{GSsYO!W}S zb*9MQE}OfF0>w|+OeePEnvPf5U0e=;lLVH5cvG;9`!5{AMG1YNhXNdImEamQ90N)c z3dP06h4O|PDx5swPy_+4qiYx_*#F>8;9MsxB;<}jwlF{+!S8trH@7SiXXs1-MF_l~UjL!qc9{TS1MY&@*cfOQpMcd!L2T@` z&FlMctVJ7VVVB21L1Owf5|ZY!b(lve6!@>GL_7pTvVfnCLLtzKdks4$rzZ(CO4HK% z8@>W?g^mbEUG)#tsl>#@6hw@*$v^M|!LVUro?~!O;afOE-YwG7tZyD7wk{lAh?f_a$!5uH(h9hw+CxtXETSIT|Pm5Cx9}ga)7?V9{b>c>zBa`VJi>llk6g zR5cK6AfMUI)m1+A!5-8Kf3b4`^JM`JGdz?8aJO`aUI-WzYkUgfdx3uz9u>M~3jAw8 zlAwvei;E_WdZ%TkJz&1{I@u7|>;YRgFtlF&tbcccwp=FPdufU6`@uH02<4oq9{>w* zg8vK@Mo0Zdu)m`{m!b>M6`Nd(Oxl^WwJG*uIbF&=X58N2_cX;_wS4i>?AMi=K9R*$ zyYTW3g@Sa~?Y__t7cOp4K7|>cdY8?0U~SQlzLf`{1v<*ggHVW{aM=G(Oi7#LKW!Dx zg0v#wsMi=eyyeV`GXsb`8e$8_MB9Zy19-kurJisRP|M!~dQvL1NP&ikLP6(ss?LR5 zN*QW=U|oPK15~ES1!Tt7CEy`2Ro&OgRFVfcJG?|w+EcF!toCtj%g41ItcxbX%OJcr z^bD?Ka}ht4Zo$jrk%?rx54tbhK+XO!Oc>7hoAInVje0!Mguyg4$a}Y4F5dr$Huc$^ z#ZfG|7M`7w%-l@nk=q)tFjk}V{&V~Fc8!sI>Q`{MmeLJBjo;;V5l#`2(;pl=E;|v$ zo1#l8aO-#p=W1AZ`{{|W_Vk8*Ep3nWD2M=$K?6l0s3ak%swec8M$mQz<&HMvC8vm! z@X&^190As@l2;aeLR*K%ltM4H8lW0H9n>qoU1rKSzu0Mc`8vFSYtd^7ISB%X*3BBd zeRE5zc6*tgixoP}c-3dRTw(YIKK^mInsXaMbX+LZ9VYTJaYpuQca6@0O7n$e>K=Wp zRPwF_5|r`T7-ifdk3x}`NcXyq(!3Z7LoF8BM@8|(i^Xq!YP!o4Xf|#pY#vE;!L0NF z94%nH{Rtj@04Xsgk}-a**%W5Kgo@|Z7lC#?w9@U*v@$!;uo##}=pIEsJ?>DF9vxVz zftEJN=X$o+{9R?R%yif;=4F)~vYyzJ#P5T7S%aQ1Wk89{OpKFHe3C@FGNV>pRhfg0 zi!6?O7MoXk>1kaLox?kcj#S5FbZL8xgyv23Y1gr56t7CrJ%)79)jLQ>l;ENswT0jY z&TcLme@G<5r!tH&bMSvO_TJ@cg03yncZs4SXZxyTSg^sHCc-YL!rn!G;Uzb!;vd?7 z&e~qC)hB349B?+Tw`vRHym=FI=LVyrMP?jrhx)N&ttBY4_ZBahT{_ z>nIF`d0b`ox47#@0V)5z#xEE3!ehkrD3tM?%Tu1&{Fg}n<0Q?+MK1Oa-XqMf382Ar z3sxK0n4d!k3*3=9kT9x+^|?nRudbh-ouS$HfOWM7V24M#1};iTORE-^e^|C#O!IVg ziuJ;}PkvFfAMc$nbkabZprsw#`OjJ{Ez#nnt?3peH?CCeWX3>+Im4$opO;yTO+0i-2n# z%=f;#i#~2@aG(!IkTeD_huDCpG3630laQZs2Iq9cI^2kMqdD2vTVthuGoeIIMAa_a z7`pYhfq={qk6qlLcE?cs8-QJ>b=AnX_a}X@u@6JJEh)I_KX6II^*Y%0eeL_)+V|uO z?P(RuFmx8j7WO065%e!{EO?1WzGF=YKQ)ZPwUF5Q=@v`e^%Cmwm-fFT-2G$aw>8xL z&{EEJbr-usx>tjZjq_?;+?#H`YL@HYxLZQ21_~SpFd{9%4fJ$;tvBT!oHVKXlB_$6PMF z+Q&)*6A=B8%Q!P4J#{3mZE{T)8({$tC?d+Xa)aXO=5D zm)nSDbBkXOj{K&`*Q0$w-O;D=^CNr+KZIBWjod(A|A9LTNj{Z3oqi7j2hC6Czfv^m z9JOmNpDoP`T&LcDTmBf>vf37v6F21mgA)z4^yszlK_M!FKq&7uR#&@1;li%n{~5?4 z)S&4uLNgGU7-H`*Y~o9H&H0;-{!5q1=xO?f9@Mi1;76uOh`~nK{CQQ%;CT~ z$y#2hG{NQo1bLT9{m;*=SW({oQU1z?Y_d67s)ilIR}ol&~@;FD)Qv_(vry| zxWxX1loG&DLXT>w6;sW~k(!cXl2C!^7- z;`Jpc?%U1y)ss`-m2B&(qE#GrL^D;*DaoAm`A4>NYng@H$mgHd&5XJ{$7O#KOHhJ6 z%h6x94s^6eC}lwwgsZ!|=-;nQa6}})#r+*TD1HE5PdWq&b`XsMk`Q7%JTzP$s^hp> zM_O&pZF(GROWZ1pSC0a+#4j82knfbA*keT3iIXZwJNEf7!1;?Z5KR+BGuxrU378is z5ed3W(t-BUa=O+DNE7I92&X0keA#8#dQ$HY2)e5^S|yx}mpgECgVJa(24Ug%4~prh zS0`50<$U<+>#tFLGEGg%R~1e$W{X{fSc7SILkcrUj}v=J3)8e=U1Zj{a<04+3H^G` z4IDqWhgV;)k;eV0BTP<--Kistd(Z0|_Iv*2{bxGQ;E05cnq9KPFTwU#$;8-4Wkr7# zN^!A2Xg{Xe^Sg2xZld7`G${nOa#lP*g$2_B&cwjOFr`Evh9e;}0P0tpE}wj)%?p+C zzwlSxWlsAx{Bl35;c%tXX1g3utU(q42oUn}Ttnk<=$Rs?UcCljrq0}J8I$`yqX~A& z0`9J^1#m2ZJ++^n6l=jKh?V{7*=hNWo9%^VDtU;CGU%Y>Q^x)n@T+HZ?7DrKYvlr! zRBn#U%`2VN_JQ{uckS(;uF>0zInUvIyWZd3&&K-eYNeule){Dq{(aZWz0IjF6Y%7A z-kf?oOlOy3G70$d*}z7@;z0MWItyF!wL5Pvu3+DWTgp%d(Yz`4r34OB8OD3}0c%ig}m9cReB?YX+;DO0L~ ziQ}}l4lP%BLdho?evSidv8R$hc43!jJX)fOda?^4H@#`Xb)24&wbX%zP+Wof#QV`T zXcVFUXjbqvJx30tA+4q_LWmLiS3(|RmATR+l;$)KhWIRF?Z_b0EK{OSMut#ECh%$UQ)}JQJlBNIBIp+31sU0I%-^rQ&*^0ET}#pUlvVU) z;^Reo1>PDWa*rsLn;%tGx`k+*oF5ZQi4Iw}#tAW2;+}Bdsj1kDA++CQ>~dI~|b7^~Z8FVGhXRd2lcLy<}_}SB;&R^Ja?thpVpc zKU#xmu3imil1^-!=Zd`&71Aumj|!{4Jel&yCwF$%G~eL@RBq_&wV6iofco~A(tUX2SYE^F!Hiw_gTG4A4(4OEuu=7(_sYP9_6+ zWRV@$SH(|`7kqsFxoS%IsJHh`zI>jge?5)mTgwXoc+j#z{h)o$?jfv3k%-8#xvFYx zIgZl|1Q>!a`vMk}G3(E8|Am*ST423*-Pu6rm2bD~+<5ulyJjW{+;W7M<=IS+_sT&{ zeC0~7=ZV(M=psu02RI5$&p+`wZE*r2P&Bnme8!Kb7={)1v^2?vj zqLGsXPl@cK+lTp7K{=g;6B=r2C~Ld|7!=&IEha|dM+FoS0quzvv6_5E-a?DQEOMKRATe{hO{j@2xp=q7)=zaJ zwj>4z%Lxm@mOyg`>jwz8p|TCbqVSFv6ltB~3$^&EPZJ;!4r+0rfnQ!0fQJThH9*|l zch^zcfl(_1`LR}#KmL=WBSBPiPiVxOQimoZ<3c^Xit^4epWYW0)d=ON`JY~S`xm)_ zcVjYctFjm$@0P;A9v@UOGbh%qe9|Q>IvU%8xQcRUdh3le(ps;rnt2K9Ef8uIsvib3 z8sp|3J%|yGq~bRVEx#L#Ztn*QW@B+1Vyr2+tK)y#@+k8r3)UFic@8C!KHIbP857=L zOG|on&TvxRI7ar?+#2A54%ruRvSe4U0{jD=y!>(fdFp-07uD0#6BF~1&hCO5R$m`~ zavx=2KwuyXy34{0b1hHxla21p^!y_%q1TU;mVv_s#7qNO*${~N`KSslTxxzxRfrEv zc+d+r6VO0_(iR=dLm7zv6X=yOA&(>0Rs8%nVPU3F(dDf7(oIQKSX>M)FaHlM>i@I* zK2Da68BSQG0RhU4jH(714axh<1C{~p?XILVk@Zhh^|*{QXIEE)L&~J3qLh_Q|YoLwCT;b!P{p>IO`-bu$=wA3_w2)lEnEY8bm#@RI z$Hi-^cl0XMjb0foY_(|g`UvI@DZZ9E={|E_GSK{7T`dUx0|5a6Ff|AWRDmSgRP&=M z_0$t3B_%sMyKi})EqUJoR8o$bm5mK7A<$e9r~NT@5d=%}d17jEeq-Xg|bl?Sve zEO9{Gedu|Npc7-oGbwot9>Fo-|FT8z5UeetljK2d4sL+7G%u)Stgc!rDt`Mvojfl% zNYu(!xWG113o=)5Ogtr0R%K@6>Ie<_vucTfdcEZMhooi&pq9$%%LG3 zGaY5HUj? z&ce-I4CwUt@BQV}&~@lGBl|~>ysN6JU>rb)4Zp$~2w(6U@yy?+j;U&V;8YkHQG;-w zp&|3iiHQjaoYn^a6!0t2b=E0=wsiktOD6!)eEZt^664<-{o2zb1R-h=v;YUDT{omb zg9D1M1jNLV@T`J^l|HJ{DZlH5cVv0lQT;N&pI{M&NNjnQg%Kq*;~6{w({6-PTQFf= zS~#aU*sjFI&uhW)gv`A1MxmNH?{rcd1+GRw%iNMQ`!ynB;-ospC>|A7rnm>VzNDd= z6cjjOJU2)~vkK&$-P28wg*=OU*+iX%NH6o73G3iijNTW$*3vRpDz%jTFmB2W8Huf} zVY>;d>2v4hx9+<6=tU|I6@ms~ZO!UnyY%FE;_TG={Cs~viH)N-S7~?gS0(31a#dvM zM6*oWK}F*GB$;iUV2XZq)`z(mq9X1 zTNHIW1nD-ZD#wwDzLy+c*T)mqWpW9nbWKV*cF_F&?GR=e@V}LhKZ!25kdu(WM3N0{ z=tS*lm!ly^uoxyrWF#>KDJCd!tybLsJ>4K9&CftTg7`@xp=3Ge@sgq;5TH7O9TNUO zSc$5c*j!%S#Pt<2zzYxuY70V5cNZRS--`v}H-v*1kg_K>lCRrTbu*MSIDLY zv}mzf{+z=P8^VtEv}Ti*3N5kcac_xjAE0|nyKV)UVg{udFYa3M8p@vx4@7m*Uipum zTxe>FR_77;1FGw0@bc2qDvFDzryB%1T1M!c?$`DsbYHGmAw8o+{De;XcD3Ow{AYLH z$m8WubHAwLrxRm!pX_A>o!Y~Pk&_;Yto^+=9W4Cg@06Kay(-|pH$qKg99}asT|O!N z8ToxM*Gfp-q;eD@u+i(g1m+D#7ci+gl(> z>VW`JWU`JumUH8^0eWQ;gw_>+?L0d2VgD>DvN1O=(by~w<#%y~_=!c{TLmF9*Cu$U?084d!+l99MUyEH!-YdUv_g7+RwT&9 zv@J|iQVncYF<*Ph&35)IvbuiXwo>Jz0%;CWcFQa)?@)g8Q+gQiR8)r)u0sT`^7v}T zu$1dyGMVkukLmO4bFZ!-Hd^bprdPJbFY}12QCj0ily=xHJ5O==l;f<)Pf6m5B45u~ z?MZ7JmWUbimVHtmGT=f_BrKFcEXXJbBO0RzXI<~ntCqoK>`m3Dz{(`XYuH$t!&W9e;-9o-1aB1bVCn*vTOR0m_E`^!*K4 zN^IA^ePeX^8(go0JQPq*$?pVJl-KL!aZPi70#;8L{UhL#NxI20HQWA9!V^!FT)?+Fd7fN zuci1L;GbcG;r!Q|0e8N+8dppg`%QGdpPGKG-s$_@n5d2@_#hieIXZ&9q|0_OL8oqo z+Suv_U1F$5_{qe{m}hriA4Fjl=HG*w3GMAM0l3rqDX640Mo z>G8jLuu2F6oKX~jz5byw-DYQOU+>7BZo;57m}=+GLofT4R3DrXj+X`7;yR7x0kKhm zN{m&@#9frWJ`K~1c?Z7eskHhU`S(43!6nj?N~EMFFi05R3w?#zL%5^6(-`3|g2b??x6Y_(=j)b!%r7|%9Fd3F6-(_Mt0vjMF`;fgnXfRCB6x zB*MUS=m9eQm=m^mdJdfQEmV#O?@)BF@dZ({@0|HVrZQAoRqLNInqVj^d4|Nb29jp$;G5#%i*A2HD$# zFYq!CD`Hst7cq&`iNPUYLxc#J5sx1omgP*PKVL`<3UAE}QTZdMJ?3JH#>j7x0Xc_bOI&Xf8}yxR^y= z^JU1J{(}*%-GcY;??Q@x&Rjm?A88-nQcp=%0GaCeYsoZhq%wnp+7dsUnaS_ttmozl zm@%~RKZj^U!TMvuGNkI8VAsV1Z!5XksPW!xe_76-U06BvF8>>-=`ZkR^cza|-IpY4 zGT@a+F_f^rB7H?R;O|aQ_}V8c+`b#Xvg`xx*Lp^TTozM%BgXLm_j>vNrT%^YuwI3+H0w1cKJN#Y_z2|Aoc#={x(#uylVX8MGHFx`lt_# zeC=6A)S1`?3}JYnf5qh$SNMc5eZ@uUn#ytT#Yl$E{w>$j6zEBn{l{senlo6(ER+SG zWvj|zrAlwcX7^`xj(HDi$2)@Nwb{H?yhB2RV=yhgB}NGcArz<0{=Ju$;7cJHvN@dR z1Fau{{blv?h%IYUk_`jo%lN>Jd0cY;(y*^te!8w!c}4cj z+RytWkKg1m6MySr`di)2my@M(*-*0J3G_7HYbktU%EKl5__zGzzIzG$L?!L}RWU(< z15n|n46WSifj1c|EOa_@Is!?Q&l?Y;BA|3>H4(3&k!F(p5!~)Ou@O8RGO>|295QB& zE0cAPMm8GvU_)(fhG?7vKddA7vGyB(Qrz=>7tSlCSYJT@a7pckp})8qN8_H*0Z9Zj zr=Bo}$#;A;36q?E+d~C%f|xrhA0=3ft8KTWDg9YV$o{O!{X1+Shc;eQ#Tr%x&-RC2 z?o91}{PtlIuc}>h{=vocmzjgwDNPQane?s=C!ao1*m~VCnO<;^#oe3iVrnDLy73%N z(EF>4+w#Bfnr4+fpLlLeXgq0ks{8gIFc8=jxfUQC_?`fOD?`edt~LZ8<(+jqBnyR_ z+QC4v#Jn*?Y*NF}OwU^)dhI@4ZV6^k5Z#KnM4Rfwemg zdKv$Ysf~IDJOUj?Be%5~^tx@g0ZXF3ZUx2}{L-9fW6)VOS^Cz-DVJ^EASP7s)j z@!d$woEjT7W(|+SP7_XzI$V`nwl_6Gb5@)r35<1hXz>DC`@+?3({657)SVPg+SpVC z;cRSf{`r$}XI;drV}b&I>`FbeM2dY(^*hG?ujFRhO8EV=Q6?q@{GTH$6$=pld%ff% zWbxi*-k$e%$KigPFgah#!1#>XoVOPwD`Jk5 zmHp4^JDT^A60x>;hs9DpH6sGsTHk6$olXBuRh!Gy2XhMNIyeUZG(p2X%f|t%DIX5Z zhL|oB9?EJO82+q0WaL{GkWG|s$39`!K}nE6k}f7pCm+ch7~y}DH8{xe0)U#L%67Qs zABmi&YQ4_0pSAwQ7-$$K&4ltspGPlIOMyeyvTE{Z*->$J6Ii;1ScLE7rAhpO%i3jv zrluq~R^38)C5($cAgCx zMVrpdxhvY&X{9t36@wxpr+@s|gj?c%ZbQR{h=?;--x-=C7B)mh$E!sMt1d{4OO><{ z^MzwRT}OukX{88?Uu~R5#Ri*#{NnW zL~Vhk^z;?!>C-)-``e{p`O?#anh4bY3S)yGAeZxZD3S0g7S;UxfZ<_}c=DkkT=?y& z|EB~Bx@#u*1QpnqCk|=S(sn%?)E7KlB|Ic<4Phiy!3V-w@S>#1uq9y)uj3lyX!mo6L94~y2;Th>r) z45zg^B7z_hf-g{r0x}m615ZByC3}5SYOo4YWr4`_3RvgMWqJ!+!y*my65R$GKDbFV$l<6MgoFqsKx8%bXS5lEPJ|hn(Y2yKzjb{|IN?&8E3KjD#qQwPq^=rl zeGd!P2YHP|P7WxRd}wL8U719HSC;*IrNeUlQjPVEjXv!E z6crW0hz}lL_!ZdSTvuCOf7)!ZtaPA?ZX#DwS6A28?$Nylw_0F^p2m>j1wj2EJv|+P zf420YJOJuvo`PixZD0Fs3{4Jm=X+66jQy~+?Y!~5G-V==fr1rIOMx{mRz>DA95;N? zMVF!)jfy&BN~PyF7rz@TlfuA){ogbv*wp7q8}K+}$=+RTidr&ptt|dRd84#ZRYyGk z?(I=`4z`<~r~?PU3<>3&H$+@I2pS6#UQf!FDh=XmCJzBE74Ag!~WNHmHf@L+3rU(WnP}AB}`M~|6fbMeCN){n1+>nSS1p4 z#(y?@)QWhX*KymX3&5jnfj-(vgQ}`56Ca)ulH3%n^67RQ^jqgjatIk=0+9^03sd}^ zvN*D^*)oz-@^;f~pclT#8)Ys29{oMK$qri`Du+;!jXAmjRc%|_t)D#lSd~%>HRePv zbrlt{>`&RRyt#yl2_}&0U4)Vr{k6WeHKTBsIV`kraIJLr)k+-u_k8u6XzC&)% z@agGo)HIR$3PSw`vOlV;6O$X4ij?dup4`3$?cF?hl3Il-A6r^m{d|3ErM&&^OIaKA z?B;xTSD!;R&G%CeS}|`e1tMB=z`g9Q7!wk#7Y$yT|NY80j9|OcDwsjPc&s)aajEtg_gB}+Ns2bWQ^VHaU#nZBvax=B)`wmK8HWh4bP4qr1OctQz zXN)88vpQIiB;M2qg+dKb4^Ff7T^;k=;3q2*~_Z=?I56JE`$Or;;r68ZIR zCJV|tv^8MMzJNK-Qg*|3=P?EzN`tCC+N$-d=YQx2Ap8cjpo?nmOCz*6II1Z%KVlz^pI^)?$v8-D1YtQ+-tN~#)R6poVzXX{>us)^D0 zsgkU5&@wdNZdUfE5YF;4fonRT1|Ga>{`WTAhcG^H$T@%@5-f`3*RBB~d2WvM_(`5! zAdlEvo(@O(3c-nSWB&|ii17i+kj;6{r%!ld5L)r}U;fGeWfueKreCXO0^B_|T=su1 z%3XdcE9-1rZ>kg09@qU`&LGwN6Th8O$TEzdZ7`jBeNbcGYAA-%=n8+wVFzW_o#H#C z2O7c3*~$fNSH2WwNK1;nx8CYm@9#Z7uQr)tV7O6|ao{NEzDaP`C9ST!|k?bbGndIOOT2{Upp`l^PyYYeMjloBsN*ag9Qg+VETyp$=|v2nc{*e zhjPexe<5fph`5?KgTtfQGouxnEuVwvmhh~C1+0Sw2s&Gtw!a3twk{V+@4WW=cJcRQ zZMMNrk-}F3jd#q_dm9rIllJc`9*NHd(zm!FXos}V{Im-9D}8V!!vVXS=>}o-)vtGQ z$E`ta1V$n#{DXN2`c|J5jP*Ec%qz>HV{)3#SZZ7%DQMTJ1qb+mD#TH+nVZk(5jyzw zc!9VpQzDv1;E*lOy&1I=Od8m|T->6<_e$P^{uXy~vTiTO<%211wK!;D|LbibUO>4|=otol5=f?j?J%6OaP})9bbNfuX)P9_ z^Ll4osWAt?y^!UCG~U#j0eh!Q$+*? z%^4DI2a*;A!wg_>83?T-WLLzfGk6q(kuG}f5p40KQ1{xuziKzA@sUXIMuOM`R7DPg zDgTSxf+#!j%z@EdMv}ci2!ny}UV2flQ&1hkbpq^^XcWXwp8zH%X8wUVce&0h!9=T( zyw5LVVDc#@W~|N3gc*wDJH2szSM91?ZJ#|q<(<8(h#@8G7l2-1 z?gMvgYr0M#Av7LWn&Hz`Uj_&VEeDqwSU7>+1&&Z6ntPC3(_~P^orxX}e2ROA3&a;b zm{yBus8#6Vf++@)4b zg!`ZU&6~f}8u6Yg+`aF?n=pU=cE_)n5dF7nV-N3KKr2FIWx>a_I$Asfh6SsdJf`{A zT|GU1j)P$Fg(k*uXj;B>`_Ai_EOua0EpFmY=4iC{OUoy;B%aNNs^dCGPyzcMy zwx^5GDd2d59PX!&>a*af+d;q}_*Fc>6Pv4;PESIj)N{EW??)Q+s>7uvw$RC1IM+Q; zlVA);>Qm#TN&~bQtP=X~(m+yp3lPlo`~l!J>4RV*V6llpfF>AK(Ch;+qkJ|W%yKT$`+!fSU0Cqu zYHMidyQWizrof6DDDN(lG(r0;@Q;3T&-$?fYnEJlCwzY3s%6$AGazJ12Z_@P=rh2m z0BImGED9hTZ~~KkxBdZW2*Jrj3eCDqJ7|!@^7p?8K*<|}K)wkipo#`has9$4eAT6f z!u#UlI+LGGt86x052pW2R;{&BC=y!{$7#oLDC}1`e{`o?r*p2)b-l0mcnut^qr`*LX7g@3i>`(j$*zlVcn8^0(LFOe+rUkS)CA}? zfR;QwJOR*7GH}J8fLQzl1PhIfO_s9(GcVFW-r=ODhVaq{jq55GJita(O4P8f%`s4T zf2!wWq4^wsxa^DV>ijE?9ccz+ju7OUo;%;iDR`Z&=1bVhgshrXj`cv}O4DJY>Q-!n zkfqIgXOKJL;@+!jD4CZ2y$r0dXRWQ5`S-&+@aL~zEIjh>+qV%xADpxc@0MAWLE=8z z5kibGL>1@IJ#=WNsAw?MfRK7AxO74PKW&?ZKyLEF+3w~UbC~GXxX)czQ|WTObF+1d z;0l{_+^o1sU3|ZuoT-2Vd278=1jXJC&_SrsA$-Hn^u#vX<$jZk+|0Vz7$g#$A;tH` zpsNDgPx#7(+5ZXZykO~)ZdYB(HKq{JOwYg|VaWuG1rwY(TvWYp&knjg&oNk8H<(%} zl`!)3T!i1GOYhA_C!#HmyrR{fZ4Y;GT@hfvrLxoZ-c*u-MhNs{%y=}aB;f~cW zaABa}Q4QuijE8smT!zFKvb>A))vtj&0*1C?VD9GU4_2EL-$IhW!%OZ&H?O6%{nspJ zh0O!^t3O743_eRwzLt+kLwutKe%#R3`T-7n$=x@X2YWjA%$E{f{)pp^_&hM6KD{R! zI*Zze4gueE{Neda>JtD-051kptw5OEH~JdS6jz&E$(y=)>jNaS;1l}bU`3Y-5QZtK zsjqriIDuRj%xJc@VVwA((E+9EQ0sqeRVf0IwBs-iy!~wrf&%gjuWN@(qCw0Z+-Q$f_ z-(B2&aD)YY?)MTMX##l}V7=F&>3~aH9sFtRC-7h&$(G(X>;xBzZ_5_V3)R>hif7+6 zeFJl^9-=3J;$epp&|-j3a;?W$AUr7n69)HC#eZ?4&TAS!oO<)-v84krc|_|H3TEWA z07{wIi&FM=S4BFC+kZfWBLRed@V|gjlV00aubX7h@qK*Y>xi(gQqbRw{-(j4#>NfJ zzY&iWsmWPS1*=jbc_DzD7ToKaN>rQ;pxxd5pjbZQ27DZ0SG|e01#6}8>q`}2f>A=T zxD&K4`C{84kijr_1prkt$rq{8Ec>qgccgtdwtlx~grct~XF-F?G63!#ZyrE66cP%C zFlF!k_;&sqqs#NHqY^(Kn)H4&flVid~R{w#ryg2bT%<%)l%SCv58 zLxpU1&gZDiYqHuAIPk#u!LSs!|Bqk4RBo8uE&y(AkJ}KUDo$kQX_&$?(~`)@D{KN- zq~l;BoV+V25cwIBB9fJw`T+UL%a;HJkriwH@fXQy`BiJNcOR`Cr>mQ$qwt}?LZ=ngaTQqE5Zgvj^w-&FgA&n!E{U$(!kG@84;e`pM;0^b-N{vxX=xNT6r-@Sf_*oOC;Qv%6DnzElLj zvoHOHOcBkYdt-JGJ9eHKXRHz?I8Tq z&VO%fZGGu|FI)w62U#YAie!tO3xqj{o>+ZL*@Jf{j5S|e?3{<5;@Io@YWB-W)i~YD_$|$C=CkGRpn|#fWyA*?6QW9;fD!vaNC#O6^k4oU zZqi2NzS=gqBIbE;8(5ksHxN77#SC+Zt&jn9>CT2k3ieM1aVnsct#O<{g1x|wgzY|7{a$q2iTpZqTVA=EeQ&pl<@k#yBb?ud=enrqvj z-tNN{e`)`GzyEH=xW>3wo%dgb!Sw;uT{Ly}X0TZ$H1H3A85Yr8;*hy|ZqRjaN=HF= zEOF@)k{cu#a5BZtxm&vlcWVCIw5*}@eQ|LYqf0C#Gks|``aMt4TyO?C6V@{%Q`a*w z%eYUUs#r2EtT$Ll7QTD9O#7p2J?Lme=ZvDq=&*`wtyoFEx0%Qe3KD1h?&)b{2+X&K zPVd2XsJAM+A?rvP?Rfq}?!HB0l695GYElbSS#W+XATuTtj={!}RaNEp^POsCNW1;% zlF#D6091T_{|+J@@bZ$|*p+6`Hof$~_Uxckg<*|cL3o(O;}nGH58(0GBl$(l-3bL# zyy%NtzrtCmTYi2kIw;I?ie|C3VEEQ5SIU(6ro`gWzyA#qhp(!X=zFE4cqpGSZ&2^pQk_vaE&vCqr zx9m7YXD@SM_GND6(PjY&AEB%Ar=IuPow;j^3OHG5Di718 z?o7F9+%!A&mvL`-nsIRx$4}xOW+6dCbX&RKroX{29k@C9`R(_1?lP%nE60l71?fp>kBvOziqHGT_HaWKcD-o3MJw27vwh+^(q3HC*F5%s$!E)>N zbb)+3?e@J1d_$*)e=IIlX`KpwGchppX~r$h?Lvp2z&DK`EB|j|e_b~3Y=16qKB4nD z``i6hW`)81FVKIT(zKdyh;Hyw-F0w*B;&8g;v;^N|qGVz41>vqkm z5q=I1>Ko@!HksTy)^X-+Kz>QiM|m>X85mv?Y3pc`yh*u3w0+0nfu{AK(i=0IDvLA( z2YW`UP5~i_gvTo1x-_39j+=RL$nM!4yBP+Cz|sfI`+|*dJ~u%C49YNw?5c>6RlVvL ze`c`;X;b_)wX1d|+CHwJ1ZU&rWwgH37_{yUeMIQW(X}wocG(Vxs{BtBdk9?FhbFTF z5bQqp=0T~cINDvJnykpAanIxAsDXNlySbsljsn@A5v1c;SRhFBOM^4M(5eitFArik zx2Bneg>%&i#Bi*`W)}D7wM>Q<6HO#r_#U#-=WNKHye?>wb)p<6zeT=xSx`7}8Vu=d zWnWh#U=={82T)ZUw(Ex}=%U|g?tX`hmjm97|sbs=9 z?V${zc58R_lLiXoTk4GsR}P65zpK{Q)NCeOd_#U<&$(k&K2McD9i=8+<||%V$!BBd zGp)YgbKSgj&$VvU0>C-r{zI)maXHIMGK~}p*7KN4l>o9P)P}~zDEGRaK_h{JlqTD(xQp{NVlSbL3i+gKvnw@oVC4p{jB9bLF}yY1mc+KrcQ0I3T3 z1I%+y+HNMHprIg%?A)nxqX(>d@qS&Tmbm^43Jd!pcIYo7<@7Wa(GOI;2`&~G7dLNy z^{#)x7uz}WFmO%sOY8I!yy*_dHFukzu!rmzS~c#NLbAZ=@Gkc@7@y2JsrDv ztYw^!DFuF?y;-jIS+M+<;`g`fG%JVZ4*A~mV`w|b6Pnc4sXRrz-#KG)iAKwvH(BYT?^-i$nd4$L#3t@W(&YCw@sUOA|hS)3@BeasS5tQGWK$ z$kEH>(z)aes$23tf{wt`-5&3jB@Hh^7u|{+6wwudzl29EG;IT(UFK0rL@TdvA-a9~ zNR4~eh&v8GSvngBe(&j*LTLtjomykzqtMz)w8GJ%p+S|~OBsqyv->W7crfIyn6W>O zF>t)n@gT?tFufab2bj3f*^=f$E1d0WU%$O(+ss@au99~By8U?2<5$Hc4R0-)+o$I) zzPH2h3itZXlnzcAdz*pRPO2FOJ>A`fWg|nB`_3)zG>n~n-TT1bOFiXC)qvBLb|U5a z^#&Adqobn}6BEdQFLTRLs_P-;f75sorjIxG`=CK=4;4F<25N{RkR`^ohBJxXf-Gkn zsS+U_G&4J?bd-M~&?Cb)w{XEW()QqRLLDzXD~n-YhSU+bkwYuwJao*D6B~OqG>(^A z_$YUGNeAbHl?ms-5Z(}^c1 z3JdSvT?{Tj+65H5y8q$>1plmVk<68yffgC zU3@gd*3ivy zBe#e2?#Q98$_RNwRh&u+MOcHrN98%?Kw#S6h{d#o45b^bfih1#drNSD zPWaZul0TCA+MxAi=-ijvR>xUwN;@RZkuE7^A?Ir=LrLnn!T#r?M(z#ektaVTS%#hv znnJF;aZcs7g>Nb~_q$p3z3({by!l7^@xmuEn{69rnmOrO9Gfs8a(0HMQC}#}s+3xz zzKS4E==SX*=H`;yMdZ!JcD^^)V%#?qX|MZYA|$;y@oodz2r2d!i8m-!4!$6jMNB5XS*6UQfq|h&6s_M{!OCv!2v4d9-MMl?#-V}a*XirUgaQ5?Xu))GEo8q0~I%&(9-WJMZFhv|a zLaX*oWZd$~oMVffIionmd#k6n$p;#qSJE`{jLdqqWN(bZLd zKxg*uo{i0;3!OFRGVE%j*yaBZm_Vw0`@zc#Pb!u=o=W$2(zZoQ$4M72Tt~lGbU0H{ zt!STaS=3K^!4oS3}KA8 z8DuU5`O0uJ-=*YE9X&6(mDk4$?P{K{tU!&(ZfZK=-o0mQ1<8!ux{sI?uO0*){#NU0~za>8oXVClfz$oR<+5_{B(de6lR;q!5qj5@n&* zF~^9_0hW&jwHS>w$#cz~+<`f=G%xPj)z$N2DI;jzpiP6$F?v@&-t3{yGbZ;oky2uA zvw;XGlTk*XZG^{|7$0BezqeCz#o)OPy^fr@d8=LRi;Cl1B^KI${}eSx7$xw%?h8`T z7SPOhgx>@vJIUoz$4-~Hz1Q1VoYd8L5|}RW)miz3U290jucNyU?>_$4{H%=)r?_zL z&4Ma%$Ad9*v$hrEZZR)6mF%>PG?=%_k2?5CNG5~m{Kn$xD8mbzs`JW|KhIEW4!bMH z<&|6Q3ZfzTs|pS;K69;3|M>?~o0Yq{Ea>yCSGw}MH&Fo+Vz!3q-e#_{32AHE z#xDbx&&ZW}?&2s2pDTUaL6oj-dNg)-&=p=^9nSrq*I@aDwp4=d!?V@4Ui`a#(+>!R z-oD&E9l*3dNw3vGHs^YN%QH;l2 z7jxdf*T2^sN<&ls$hXRG!6PBD#;1O*>q2A+r#RzigT(OniRSLq*zzjJAQ|$V(iBD% z-937T-?)+G=*>}2?@AT_$?2%aiUoHk9F+ICCxo4R>8LDVcdCh**YHFWv$A1v+k^2n ze_56fH>7zEQ0p<2zPMw~W{_rAD8#|f(q4NacZtM8^(yTUuT@z!lw)#o4&?W70650M z0e)a!ULL%(VPUsTjO|iRdH7E_x)b_^=CgI|m$XAdLSWqOA^-W9j>Ow1_iXRL^3GXa zQ?z_w&Y{IY_GR1W{rrjMrO<8w+LPjM4)C0=)dq#j^Pa4+$~2m+p6SV+aVk+S;$02_|~&v?YI4WU^?PkxORG9nk-)1;k`MC7^D2liMlCTYjm}sG-7S9*P=u4i>cO7Ou7`^ER4;>#!&JjH_T5iOmxc=s9U_{c% zS0@4qwW8Ped;`;ubq+l>k}lfoKuhJ46LGXK(n3?5a+Jbb>#koJE&!vF8ISsG(@uu{ zoh`S2XbnE+9pqIjx>a(`rc?j6@{O-(is6G@X^_n_NZ$@{9T}x$g)WhJT+V@Twxz90 zTe%Z-LFSX?2Fo004b8-{m!H=b;|Zvu1J?w}%;?_-PoF;R;NVaidxUywYh+%@_3QkB zyulxP1o+!&C^fM{DDC=Zx4oR=kNp{iE3~KQ@r8wB5P(1clUGo{KN#(3l)dN78VhD& z>V#DI`>7`{PglOO=!;^?>&yU)QjFE~>|14Jzw_EUBX-r^p7nZ!@?&OGj=Bq1F3csr zo9PV6*v-UDYbccSrrM|V*_(%cRf~4cZo}C@ej!^PmC)~!e9yt@6vbPkdu48N&Yi_> z#)_tnigGNBLi>PzivUT$=&^t;jeF9$k&;pWPdyUH`IeVS*4GMzk2daFnw3+dF=)_t zGB=E=56qY}+j9HB1qWg+u4BDityo}RMOzQC|y=NM%!^`jwHZF|QGN2-vEPTWHf&Oio=xWC6^ zR7qa`KDwoaQ|?=0tv7`Wq1&2)6-r8?_{&8AD?$o2D2%&&Ig*fA+MCpOutmrm$xutZM*XF`HWR5y=AD%0e$h@_Kw;$3v-=Pg{Lmp zp9CM%jte^dY!_uu)8RcLc1*{sGp$;NnymQ-Xjx7K*m!FR9-tN(a-ZrDQTbV+?`_Np8tvgZcUS zm7X~Q5;3+XzQBL9{a(*2mt|ObQ}X|2@zfRMC3xekz8rY9M<(NNe&2h5GiPR+ubc$! z9%wTiGqCNWqI7h1pRzdPQI4^c>3ZKYR}JZ^5460R)zSNWVywR9f>Ne?O|jSXRJ!zy z?(D6Hv?r31+RP>9+!VHmD)D!HasRsUtKrGt4=ygt=iYsIJ8#Imvd81{!RmKK3Hkad z#MGysZ(Di%w?Dedd6l<@*25LqdWthCI^0xz>;-W~uhxFvY8OMH#*yi&MtkG0p<+~i zT%YuBr|XujTw=HS4Zd_p29qA~^^8_ouL#=bhg*nIA*iL3;6=bRjg` zl&RzXN9D%swSFhtK=u^}>WEpVd3hs+J48a2k!W|mxFmExhBQVSFJ=hQC+VS?=kqg6 zjq&F)16n)a@`fk@7jg85v+|xj97W#Mm}H`5^ZZ8q0aK1ObYjs;z}+-^7fyp12!K?UxVqEV(e&pidz(P0oUenE|AQ2E#g#Eyclca4n zuO4wwiE!QGi=zY@Nf;h%Se&sjytTuO@a?t9kKvZR}k6WThn99SiDyz!aK z2{r$wju%rWPx(~cuLEc)oHDVUs?Od-TiYw1v(0pe;u&-MUt8TqnQN0yuFCY3wmk^A zt}1P37rAxBM)a6iA`vrf@^jaii8;(Ou4{hHaXk_}-tBqe^vL9+(S<1~uifK(dC`lc zD}n=V!@S(|cbfN@>blAr;f!P+F>lH;S)v*XGpJ2YPjaob)hW?Ebh#)azaT8{UP4uO zdESwGHN)~1hm+r9SWZ^UnCmY@7gmXXv*4B2SmBZbw$+5|R$ksX)fivah~@*Qd~h*! zI>^dq8QO5bSMW9_G0)?}54@}!`w9~c``y^-(3sVG?T8-vgY(SH%msXq<2F&Yo1GUmQv8@;_3k1g0EO-%i%?J9K*JOL89;r| z3A%#3GBGhRAz^08|FI%#SV=rMB?-Fu19O1rV>yWeGNM;nK$-r^miRV zFcoigl5>ZnXu;0UdzBK=EyQ|iFz_bOhA=~^w@Z3wEE@~!22bdbpTNcxn!{m?4ubWq zwe=5TTds zJbT{8al2i9GqPIVP53V((}=C~qnyxrKWOp?sc&VQ3SZ_kSgMq5(4>0Zxq87sUQexP zw=)+)1@nu5AS}H_B zd<;V+G?WRnZGo^FcK0J=$q=8>-FVFNqX4IK`MFHtT8Q)@699z zN1ZCvBtWueGF&;#)Y3AQu!J$g|4Ma(U=>#Fy~#U_v-p&2DYps7J0}^k$&#K^^z-%C z{qoA!I<)spe}Q6x_7A+;1%lktEjBq`=h-~(SBtm`Oad8ifkAW7Szb!B<49ROefjJ7 zH`_Z`r*g<$>qdE@PSYX_78=_n$XSCwVI%czWhLTl;1yHxg71s#zaQ4WFE?5AS49q9 z2hvF`I4Y0EJnq>{WY?|=f+|&QJ6Y82f_0Y3h$p_H!Q01sb7%#It623J??Q71zS&I# zuLxK0m3FH0%3rqaqj@vRLRZzrFTDjFp_Dt ztJ8%zapaESqZ>2c8=YR;(~?bDTCD4@U)75ZC25tOFB|kIOm3>Q9(!$jQc8+RphEAE z^f8WO1tcALJ-PwSZF7xX5t$qpi;bQdZA+wFvPa^#ZoDCIRDzDwL}E^l(UGO6Z6oo7 zX1#E~efG_}3gU0cAA`dFn4%p)I>0YL;i-o}Tkg{b$3X`l2&`HM-)$_fS|_A=Ju98TO|0phop6~E(I z1W;G<@~Co=0Bm&JBIUnhZ2y;?URQLVDE;b^NI$y}@+0?1N)b34`Xjo$|E%S`ncq6$p) z;@V88@7Nfw|H`N{BtH8)O?I{yZ&Dw0e7JYN+vQBND|5b?#xZlunVJ!2@Jr0x1m&u3 zm)^w^e-0CU@vV_sy3Z{l4RoJ#ctm{{YhRJnJ^9Kgy)5ILrF+!=IKhdf3%@$$oquiw zcA6b_yYPAYxy=Cp>zeKVk2+GMgACsFaO0)OjDLWG1v9esn<04&gKtz%L@xC%|uTcmD#=)3J;5dyn(M?)r92@zEg6 zbX3$iUvw6m6kH*A-%{w=cg?L{Z9R{JPA_yf&+F(OrD-m5=@M|`dlw79NVBY@~XIA)b*=}3HnQ^S0(AA zE~hSuTU6fT72q4XKI zcrq8(Y^VRR`q;rtQFOsv94`)!Upi|Ueb_6)UQ_dK4r5|iM+#>;jf=6Z4JT7)UfY9O z>e|_FDVx>o1*R z3RD)Z8wZm`tnb)XJGMllQY3!P6;n`9>^a{?kN5LjxJp{p8q@B|AQT>pWy^yqW(O2H6~pI!4Fu>cn$ma5RhwELn~p zlUss}C9}NP^6$eSoRHVf*FeUKR;JC;Omb_kD}`-pKq#HRVP;2@Jhxmvg~F^{W75ez zPPzg`7rR1&;)%{!9wRCCVAT)J1qH8eEoNM0laNSbGiQ)UVr65H2-}43SlPDETw7KZ zOP8=DGfK`*xXM~-_*Hs&wxtuWt-;_68}1IAzJPk#>j)p%e$0GkyD>Qf4%GU@-<`(S ze?WQ{p#V~O-$NeSgzp1*djsZr4oT73ikDr`ktbsjE@?AyO8o$`u7OC)&X!CY`tqW1 zbr8>uj1mttTC@#qb0T{Rq33!7U{jTahAINRAjnSHK2rYIUf*5vY7;Kqb<3Zh397%p ztUnw_Gt6o`H}$KlfrjTW2TJdFE}t!@FnY!*%TOwXL7|@8#GvV(nmdEU($3$71r}Nc z4sF)9&Oh1zU@o*>Bqa6tT6W+u{}9)fQWA;QK~|NyCPwY~p3|2@46`=d#N`nft$Sg< zLbmlpNj2zp36aYWF4G7V!>AE3uD-3-Pwu7RV#25|0K(q40v5@K0-pe`pL>8VDB46t zQo7ND4KqL9XeYu(K>{==D5wD{zJP{j!;W@9^GV^|$4+f=7`kKNf19%*ejF-;a|1;2 z#}6OA4&S3%=f)P8+De`FP*dpG)=xJs+_P;`%}h3xymYGEV8KDzP>NmZD`Q*1rIXzvesss`n zM0NmUXbL+oicv!1YLCQ84~#!N>T>H+hlEPvgXiC zm)g4>Ini)?^PsDKYKtxrvt`kklYa{YE6s!@vkP5CiA(fWGuFz!J+RvoBv zbY_NHADL0WrOeRS_zyPWH=JJVYryKNM!t}z)j(JRF@Yb5j1f@s6jD_U8Ym)+c3G8^ zNafi4n1a6xS}}ru!oZw6*1^eXY9NoPed&Mscm*~sK54WHenT5y5g8NH{_L4mb0r3h zWLav=5Z~aGqeax;-;a_0hu&M;wr(A)IcrB&I{+Y;n)LEu9e67+%D|+WM4BC~R~Qv$ zCFpOi{~?>l0&x%5U7z+*8VGqZgf~?Y2bUdg2-@TpTTs|gx%fX1w&{1_(R-%A);~dkc2wH!G>wuF zZg<`4kaaN6^3c*B3^Y6ld!`2_m$CDdR z{%zdYxp2K9*-LcI?^Eu@n3_}PzVF=F^)j-MBb{4%QqoEpiw~(nbLN1vITzE|=U1fz zK&#*U8uXIL(F6nO`*;<$Cinyl>|(LnWC$rdt?L-0Z>R>`N2F(_g(~M9Zow^Ew!p1Z zCc_OM5g=Kt`uUyYy7F2tjeaub)l6J6%$qi$d`D=I(=o$Bl{d+fz#^b?mLEYui*_wE z=)g66F$aBaMQ8aP1pA(2jg0PmFlb%;47uX=o_?-Jq~a z#z3OycJ9(JQlZ;CMOI}fi*HPEv$G3xFanlGBDsHi@<;r3!?Ov1zRt>W8!)^%piB z{2mcxtSSfyWI$M1-)+}ccSR~vq}Dqrg$OKp>`%&NnH>n``vwxcxq_oENakM<%>RBR z>125wJFSB$vjJ*u3GWZaMYGooL|+ZcBQ0cNVj|lRIy;MGbVeD=V*4TCLGS1%pz{Q^>3fQf)x3sjtu=-YZ_M7}e zETtC8AWglH5}#ss;GFRpG(Yr@3`dXjTRWI0fU8Vk=K!*TE~%B2>U}mY3NF!`7K|3E zaOBc;qV}3udRV4RiLse4r!4Q}pAa;Se!Gd>h9yqhkNTH;xC8L@uJgRFAqrU@jDPfE1iuc>cpc zh*?}7g4qf(YFw{?qrk@xWUa6RWRN`$8~SM#lr6yBBxPqm#PcqGmt}4p)1I;KVK#01 z5}KTdY)@V^KO}D{f1LyWLOzDjTaIvTnEA5KGE4PtI_-GmL8evb(9~t(WTKavJs~H- z`E8HUk^ciGNO3Aee=K$TUDmoIpAPAy9&MvOHQ{FAW^gB=W4V$$w6AycT!Tl@z*Ldv zDYOE?+~Jb4sl+@zd-LW!S%zrWlS0AB1QI1tC>HUbl7#yZOL{2TIWSOpl$n7cjPVpo zFdUf;L~KW*hL3ch)Pj;kc1!`|1MZi9{y*d-X2C8{fCjmH>b&xL&>!F?C`jPRwR_nY znkT@P6nq85BV5a3^gtl6lR|;oF$4l&xY99gA2SJPB=W!p>_w=dgUJAcA5gdGL0h?Y z{W_U6Dtw4YY?6HgS=pW}g+pYGAqXvn1!3wv$l|k6XldPZk(LE)CwT-A8Z?C3~;G+%LuA zww^!pSh4j~9ghd_9Icm83tJmW%}_;ZD5iJ+lA;g|?Kwa^1&MG&pCF#Td#g5U@}uP$ zQ}zW@Z%@rdN=8!>Nz5TI^pJtxAzt0Y{wv|to(+A3Yc&`(I9QXwypyQZ-NFIf!64`5 zHP90g@$o*xui^26gc#Nd=NynF<}5jZbialfQOjPcxx1hppmze88UPGHBmG9PcHz@2 zm@>{SlF|&;u$OTJ~EkY!K+DNl0dd{35ncaKsWI2@Z>=ZBNt-0Q;>()%e)3f*~i-(xfcQ2 z6xgSC8uqy&BzYP=M=bXkuGGx$!#vO*-lHkjkQete<*zawYJ zc#9)3X7}^s6K)4x8JRk@RHzySB3`oN?5$=B#>vsYZi(_}k`E3A7=-XHbCnMNetEX5 zt1?UW>x2BpX`78YWU%}2j)2U>Q&}E5df)(peGpLo4<76}&S##DOB|9x zHhP>SQ{3$t8Q-zmUqljmT8jppQd zq?ITber;v3g^W@=OXF(V+yCF?o#v*7ac2a$zc6|%oHp5YAj)pLSR12ObNPwFn{5S| zCQEc#vlrYGY795at&^6BiT}^~HNH1BoHjko z`ReLYVMBOQThe*H+nO|`B~236r_PvX4@6l>(7@;)x$NG?c2M z?b?<2f01Tnpjkzf;S+=9#mR$JBMm5QK*u9gL_{LnoPY%_DkuQKw7)ibQt1yU4(Em| zbK%{F^!^+uIp^ld)N5@uN}7CJhiS&r%E6E*Fr9w+reStq?}^!$ro%GMT!8fr3LMtg zTu@96_crfYu5|UGUkk@mfYEMUP7c}q5l<}I(j3Z6z>3|_s`n%&wUn2uWENrJLI!r@ zP;tiV+_CzO-W-Zh^@d;$9b_e6Kw5?aHS$tWa4e24`j8dSp2p;i>QGOmHDO-nJ|ZWv zPojs-I$>;zd9+K?EaHJ!WVmVknfPIyabI=E$(O}jg~qHN4QA3s$y#iwecMPTH(XOp zB1HBBiX0h9ZF7Yqtb`e}Uu0{@+qybCp&0)Tux2wYDRNmydV%EY*p>&GLsY5;Q|qf# z%|d8S#-H)c>$b54wLT2}e z?FXZO!-(C}>mOE9=r$=izB=x0x2JNJB46j6^MulXlf;=}cd3|&BlcaDZ?gnhT3k=U zQrWoZZMTqq^jHryX?R{LS!#~rTlBOc|8C(kM7zhSWM`gVZBEN`H>A?rdn*c@sAHxl zm&8e-dfM{bq3tW4N*pN+ky>m(0{rJ*6N5jfPqi^zTIa|(%n+4&;K1^_p!zK~Mt${n zEi*&)KiPMS+SYKAa?WR@$Dbdc7U-*N|E+zi zpS((*N~C=qa;Zu?LVH;#)|_Da{c>?VNb%8^m?z_5t3UxsamF`0#A@##k*;+|yQ%jT zw?qV}3TAX}y`;^*Ii~j`6E)@L7^J}X!{c(u22wNX2Ah>zrT6`)pm}mJF9R9LW%!mK{=3)_A+8) z&%AWo0;}sX@$fL7*i4rq^y04UcFq(^3d%>^+W7sO1ldAZ>_K`j*OVT! zT_sgP77~JpEHf3UN%T~s$?mFCNqU5Zi(}=o*f&gx=7QKfovUpf^<7Ulbk0c1$j}1s zvN>Gn^x!8*qsefkHZO+CFJa`d`iSEmdmc=(S9a_`k$V6x0n=1w}bi>}A?! z)Z3*?{yQLwA(0pEbJ%5wB`QG50Bz$*hjg~C)ho3y?Fq9m&@060mgJyz??mt2*ns>U zH8pER>Z4>QQ{{By&Y@4THv|U`5;=gspCB91I@vBbn29;)avW-MFyq-C(VX|cfgKU~ zdoF5X%UV2KoCcm2rLFHmlTJdcLxh5&X;aP%i#E1$*WO9l3*tV5_)?qc;B`qoaW#Oc7saR=GVKM8$g9N}sa=(p-jpBk4Ru-@wB zVvI6YsT(mDTLPi~rp)J{1u623X7FY$?ve=yr*)5d<-eCx`-< z?UJ@+8iWVn!W5-N5w&M3OQ`k z2K~>SBMq(cTz_v!B*k)EdJ+3gJN4;YL7E7YfmcM>Nm++LBKVTn>(3(?>fg%O){;#7 z+H|1SU}iJD(4RAP5LG;2x;st8K6w1mb>?|NDF?2X<01&Tm1 zWTljqA08seUZSgj^!}BlS?P+P*YdJK2qCE9@_9{^!PdRzP^n1H=B1&X-nb;#gF+fD#MU@6 z;wg^S*-Gn{eSfM|qZ_f6qtdHE+mJEJIfC}3bo9z@jnGYWH z%}fj#Jn<7l2El=W&xZ8knm|rzicggNU#O&{R$IPgpx!tA#hrw1`Vz7KZ&@0}c2`be zmq@p(r)7UE8sxVt2fXi5C(r@`0aVU5_4n$5tv#L7??3w3Bcg=1=L68c zeLGf9RjE#!lelf+5fxsdP^Km<`PALzO!b%FK{dK9;45_qld7_4NDlI9u6rp8PuOJc z+L5Ou&vx!*Q@68KakL1xcE|6VclsQ%DgDCI??j!Qhh)}=Nh6D2e>JiUvBkPRIoAw%6|K^le*<*SN6T-qeinI`N2pN4gG&SyPP^jqO8` zv3^CPHwE3wDJMUCZx7pEROo(oAbllp`F)llI5AlwDJ$x?5>6E1!`90X7~ zD)99n^+I!ysmWg{DGK1yh5+N(B@JF zgATG&hqd*->+L|*c4Np44LCzXW+IgloZNp97LfR#Ki9KJd+@hemf0fNRQdjYw7U5y zRQjfuNF-TiILh4nh&B+oSq6G~v+W9ZUYFXtpPsO^+@gHrK*i4U%38(3U;Jc9LaL*; zt{dL-+j%;|Ts$blowbka-rM@}xw^ZF*4FJ6cSL#l0!XBAhgYtW`-!an(|Xm}{jQ}y z{8=(CUfQh@W%hD{U4$vO`6>l7iA2TyD#V~d{tchNX%UNc3wh!~+Q-Lx*M{EOp1at( zs<*HDN*$wvtu56%)>#+#2|kBXHs0$irVjtHwgpV3s*8ShV7^7g*MPrZ8*+AnH}mc{ zzt8hmvbQBH>mI#-cyr7&fw@0==VVS9Du;g%Ryd)ksHg}Fqs{AD{U+r1mEoO2`sNj{ zt*xxm)6#CBH4b_hpc6u|KpUdt23i#8Xv4$9rWe@5W_?XLELW^y%z@X!ZlA_ez?Y^l zTNw)!MEfJ@Ijcsx-egVs1P2EnJt{z)_uRz9bl5#c@BAA3Mq|upEt&p3#kb>*+e%7F zY)MUL?7g}5sp-C`r^S7yu6qmnu6A^!S{>HW7CPzQIDOtJP>sTcI z-yy`=vu7d1qoYF;bho176gO>4Wo9O~QmTo8K{ylkrXJ^xyPKmOdHML<+}uX?$7W|| zL-O_s7~!#(F8ync=<9=zjNcg>e~nKp-}p~Pp1I`q4&xhaH(tk3))IV7{9oOw3v>@g zMdcbQ#2!AFY-73A?1+A9`psJ&vAgIBdH4;4Tock$UQqBy*>Z0&I?g@S9(Y2oh-+hM z&r-Hnq0vwKzX6F%C!c6>Jj^ssNkyL}TYT~|+YF6^3SnfwEhLl^V69@BZS z&vxcNiN+UL|H!Y|{y#IEh~sZ~@1?bPMqXZg{NdNvv-SJBw)xGC$?##=b>s+xksWm0 zfJv>qn}Os{#RR%ZD*9KVk6Tmgm;s$~Z1Ukl<$thlUS8hQ*&n>?!6L|BIgp#!e?o{g zY7FGzNXL%def?P?ZpynJO$*P;yM3pjUo$m5-FJ?l>l^zK^cZWVG(vZ37t~!F zj_tnE4vt~;@#hE{*x%}tl>@~49Y#K@fqyQsPn~GjCjLlxxK2$frDV=_be(TGWR?DC zAwB27s^n#to_hww+R|(?o1@pgm#%Fzl*+;p{K~KPpD%Iy;aRNR|AG z+hME0@bSm5h2*>)eV5psVyKvyRzAM3i{9k(;k}ZALdRiR;@WoB*T&d!79z@ihV~XB z9;eYYG_*=r+1b9|yTE5y_SEx%yJ%0+|D9Z?=;gX8(Mr1ijrt%JZ#JFdpn***o7J5M z6P)b5@1NR#dA8+oEdBAw7scj3I2dnfO6^t}!(H+GC@_E#4S5;Mg z6|8#bR%*P*KEEH8?`z)`(6Ja8iP@K~Zrb*>tHW#awy(H3QtxnV(ZY42qcfGa0|3Oj z+i|S%b7uMc)6YiQ6RQvDMoY>2-SM8^yq-$XQNkru- zsmKj(R8V_(b3?I7WYg*gT~D60beMDHZ+9GXwYg_2#o0o65-B)J9%@RPvh4P0fbaO* zC#!6vqPGVHscYzYKHWt5ntDTReIsPnj)?^57;{S#J{E1pne1rMf{{V-;L3=hWd0kDJJG@|NaDv$DbG3j%$w z9l23;&>FPE5FR%W1*v~v0J0DGf?AuFo9~yy-hHk!A3nxxEXS8untoaHzF#$l1$dU= zQ&9XUNn3kWi(((#llmTj2g9N5E>H|F5`4j1UV9tlQbk;y?ae;ZN<)Cu%{X zf-TRgeZ^_IRTY9dJQ%7j*JOk#9)Ycq+wxSbVEB#+(V;!0Qv%rcwi6lK)=0`V$rMJZ1Y(KpR@)0{VH2y-q3E~Y4s!K~txnFrHi7k|ub*GWA<(z+D zBZU?K1_)X5Ck?c;w63h>T6gN)NF08+7lVe%A*-NP`j2O8kL)z5VTfwnb%~zRYyIV( zk5@EWRs5b4e~!?S6uk20dsOUvd<4dVM;YeWIkUOOUwa?Fux-{Lm*Q;O9n)W$TjiaFClOF4({Xa~72RPOL9{(XBD50|5Sl)u+tDg^7`AQI(OwAR&QF)q1Mt z^Fub$9&;BEt>N!h57+~0R5;0Qyp{O(*nT;JkF%TC(R3|@^7rs?+LoX+gBx~rDw3Q| ze{lMDhWY8+{(-j3_(?A-vkQ}Xcq_TpbWJC5_~}E+!~cXGh}`5h?`u+T+ zhb`ruW3G?7371-mEFcWB&g~&UMw^hrV|a)oAff~V39bGc&x7Dh%CI;F;-x#(-OFF5 zPuQZ5c1Yl14+$a@WXVHo^Rp`hjjyIch22zmJSrR;d>8HK)>)1(^@ZQ%AhjOO_}Oz+ z@zIv(w#>kM+c(bhFY$N^W>#i@3WgJ9ZaW*4(1qp1j^WAbEB7*JkNmK5&?hIzp*4;4 zVI;cGtCRE3>&iA~JFe(*8qhX!HOSL;^bN8$Ky)$e7oW25ahHceyf6$?C`GPh16VKr0~Y~5u%x#p05vJ-wGk$U#T!eS-) zWvzya3K|Xvi6|h50;(Pk-Gbxb707Oa5DfYYq>WtbdlC~L4~1+4NLpcOgGKZnJZ<$i z&@T#}h?O}zwC_XyQ@Y*D=epZfmG|FR)e4JGFJFyu+jUUHAtQ6i{)g6ig~bG(b>w5y z+3~uppbfB%0?25k)(n;6Pi?VdZhh>RZ>V?TKiBQ_f2VlM<- z7?4&0gL^gLE1xYUzXa<-_liqg-?tiJVmJ>r0qSuASRK7HK|pc8oTvg33Z(Aax44fh zK<=#mQ!4_o9-tHe#ejw46A%DlRuE6Gp$c8BTuQS-WCEMMK-C=W(!75RZ77MO`DmX% zWMpLGTlJUVN0lIEvqED_8gS^rnGwD{yI!pa^g2=Ab>PB_LqvlD4U2KW@4FJ zPQlLGf%J{=sTfAB!n&S=46z5wMUQ?$X$M{iXccx$HE+0}u`tk+UOma*g037agcAg@3+H5Ho@5|UJvogWLX6`Fu2Ko)h$ML+L-CR4rlU(Lg_GEs8OE*g9G;i1gH>s zpt~!uyCLXDx8qdv37zSS6FN@#+zIN^ceh(VfnM?MVp)NDpX*V%A;oQqW%71$ydRf| ziXJ?|FVxNtcf6^_v`0kffhGJ_S3RilGJ4Yb5K=yVwDMX-ZLL@HJJRwA7p=ekn$BT^ zmS~`$&3&zB&#q}d7;pk_ML-Vd!Q_GxM+Z#~AX9eoa?%)a4Z#8z5)|po&mTXU0J+pQc3(+!=RNq zx!LdBF@=$~vvv`hD+N~*kY9xm@dQL+VjnSmP6bXqa=ND1)+T-H1lr*YD0vwvmA35c zY`ZSt0GlV#-7}Q=XA4YZF)&>QaXWPH9Kb;T2oYVFQh@ZLK@B?psj7mGE-2=z=6iyL zHS{_QhSne+<|a^7QW~->qX^g-{xTQ&knT!VBPeR(p|NGK9QLpaaeH&WjwOHqCGbAu z*fktr6%^{#L5NVe^sl$MFB<#Oc{4dqXR?__6(1H_S#?edEn9GYiu^?vcVGoT!2ISV zWG0-@cp8U7sa!~c1R{R2P`B5w2ulNn3IWGn2%xuA9iac}zFBDoN-A*29j)dUV`?4z zI|Z278EB{6d74rIRL5ZFTeoimP2^n`=1X0;A6`v3s{vRMc3NgzZBfxCH2kYqJ|?ds zYio6fv%vt#ft%?7j&%%_2OMA!X@Y8xCv40xDfe9?7lG7ZG|Nbk{Z}5ens227hx&vx!yLmER z9n=4VY%i`fP|Fht$R^kX2s`GRAp;pI~tyRs2+`mwv zA{vftS z3_S5V2S&QDCg@sBJh&=$qaq+H7tU!kybti%oZ163#JQ*K8MGplCQ@+1OF;Mlf=Phl zCL<>=ml$9;9?0E^Vjg<}TDqW(!O73RPk(;cTBgGH-Lgs@dx61Wl$J1BGZKqdC|(C=~kgin^_Mp|sixdKMu2 z44?oQj-#A3w^aaP{2dG-1H?vu~rwjbRmiP{|`|E>>08K(Xng`e*ZV>e% z*YmK4J|-<))vgt8%@G<>|C9(1wY;TCiwHYCDpW0bVgq{)z`1ru3O(gbX?0tT`0yPo zn+@TPRRSV+Da0CwhllV%eSiLzpYx@^UQ7g;yuY9eLkSGTUXJ&HiNa+zquf5TL0m&? z5+jQmOZP-$CrN4jwh9^v9 zcniFxZ&L|PGcXn;sr2?BR}AJw0^@c~Vt>bZVQY$PR?I=_v|bIx5;!9Mfh^UV1K-B4 zwY6xOPUIYJW>Cd`;CtASeg^y#KB_MG+o|4O;^8BRp+JACo&zmA3vml1Y+oJ>5CuwZ zs9|pmx3S=(wqPpJ#8$wsqD4RfsE9sT&`CaQ`LI$hxjM=hE)HQ6p^^;hpbRDZEZy@! zqldnRZGo}L`{d;F>s^FhRJS}Px%#o6^bCNPfu#V>(6FAQqL{va<$D5Zwa=i}c;^n( zvj+zU%ch3rw$ajXprh;x%myi92>gif@c}2bj=j6o113bk7=#QZ8cMh7>^{{4d^S9Z zpx=Aa$=l=S?=K~bhl0}0pYOZf!CbWo5fKbXdLV~+1Ser9& z`**>9NYf1H_*ZXM1%F&2x;k;h?^o9ej?7Pq+`d@dj#Yr{WB6ku(%3j0ujW)sUTqH3 zDfE4YQ@es2YR`<3k5dHuY|G0_OR-Wf%RYmTrsUQ~NfAThjKJL_EU~s}I-?zU^xoY> zcJ6tbO01eJ_mAwq77sa|;iDdHF*!N^L9Zd73T?Cl1OyZp1?qWBy70jLQqY2CT+=4| z+1!Fyz_5DN>1986HmCT`#P{SJLP6G8)_eE1L5qC?+!H_zbT){2T6th?9}T+#(9t^g zx01X(3lP`^;zEgLAspQq7Q$OPM1i~0Z>}DD)b6hhm9Rm~Pi?FT$17Y)0M}jbfQ3U_ zL@@eg_t|ey!)9b;goR0jWGV{+cn4k(>OoNTJbSgXIS&b8eGsP2nGQ%k^kUo%+-x6i z{;fK`@}pGqEB@GbIXEz&<<-&N4k&^9HkhazP)4d4Bl?32qBGw#3!yu*1;UeJPRq*E z*y{yAj0DxzU;da^pe+Vxi;p8OV5_U(XjMlB3}3&7eO|foZ45K{Q$YdN(i@JELped` zhB$SfHBWJRR0c%SP=2|y=-ZjZzb0{h)C0oRU?>14C611c0yd*gLprZ8P$1?4l4avZ znIafzm!Axz((pkvZ#cxtqRqSYT<`2icPHyhSEG}bU9lvtFOZkv36ca}B9pL99ec$7 zI(hw`2vt=ypx~xBq@+|kr_Ng#G9(~dTai8Qy_m-$_f zRea`ep0-#3niDHzyC`OH(G=)Qpd$kpI)DjzctFn*(187+)BZAbXgYwxcujQxS z7!eSBr_$`L77Z?>zl_%|1uZ(o?0zNjBV(vhpk={&KOj0+jlS4fQ2ksOieak=(BrKa zK@Ez^putwSL2uvc2h!INLsJd5Uq)89-}H4vjj5va=6{GBH6D=;P1tyFtcAf~p5icdj^R33I1kaH}IQk|HiV z?QVegZ#LWp=xOTHCAepy5Fi&$H_?_Sb=;N9GoSGIPNwqkuI(3{*FJVBZEbMOlTP~E z=!%$@2;IC13~NX*Jg)~C1E>l}tZU6Fg!)}A5JBipeBmZYhL#mv@@}wU0g9JI%FIo2 z!)dhEQa_{vtxcm%Ct#}$oD*=R0Qr7gU+;sSd=Wk;#RQleW}KQ9D`)wt z`K*Qc`SsDVHBcD`W`$n4t&YC%*EF-CBD4__~dmqVj!g3$@1f!QME(cuwu#T#S z)(D*+a<+$1@{TUed>g%O$&0yDv;te3jh-a zF7nU1U*WvLLXEqO*?^S-uB9Icn5T(WHeH_rELuod*d#;=uuvaG(Etx_VeeDZ&0hYn zU=YMdN5c?f!A)WhM>3Qpm_^@ruL!(v?``Dn1T;8^$qww2T2&$HN?e-1PCB^y(~(UM z`yS}kK?nliQHaO$i;L^#!bC=uZq^4AEk#2%52jNP{YZdbYFr!{h~tCI7~C1&yQ&rk z-I(OUd@lfK=L#ZDkZZdx#Hvl3Le=AX&^+9U|3HWieD0fOSXfy#;VK-%bbsYN_R1BB zfz%&}4^aOp$xDRWo`$Fu62K9XN{D&@bzKdlE=Y1{i`M6R$>%`xR1|u?4h}mI{D}ye zGY{dl+M`g__)a4r_ygBS>K@Jg;Tc_@bkN)e;?8YcLja<}DGK^hy?a#G39!#b%Rst= zow1K!L(uAwe;ZN3S#U#V!amFQKsj!Bbo3YkxPYo#@zW3;TrD+`8Vw&np*|PP0*)A) z_P9-I%u)fN@aG2kM3gm|plSvQuzW!oLwYKZ6!$V5N(z=p+Pb zVst&|3kAT5^*2=mA7liF2K%;{rXnF<(G6}eF#+f(Y<38a&5m{!dYwy1E*5@)PW3kc zn=+A3RI77_Q5vQ5cYHOJ)qeIY{P@>bme_Xq#c)H^K|-CCWeD1`=mM1A6_OZ*+bDgw zfrtW)AkTtk&a3JP;nbpx4Ah-{*1LB{s+{wiW?c9B9wL+haQt-v{xbpKs}Lmsd^@r5 z^ce7v{d^m{N7Ef3S#{B01k_d2j6QRm?(2nE(2>6|=fR<8XJLWjA_UZbT}9Dh1)#vu zz6bS}t^zBjVQnAnu3}x{Rd)^%i+9)YsaJr3zGfkyCc;}m2|C>kS-o350mM8!Fh#we ztOuVB6we0_U;hLsF^Dc{Xi!=?FyUr3J-CDlk%Jp&|H#AH*;%)EXu$y>iJ%s4^0H=M z6=Eh}uz-ygGY-JyXm#08XonS)J_Pf&G^9Y1`i8k5*W%&fXwY4 z>36do@@x%2^8=O)fJaJzd@e{s(B!1fl83{c=o&5>6%!#Tc5H5z*Tfm9>BZ6RctOtS z1u%9ANEQGspl-W`I+GAwa88d9407=a#AW#wy_Ds-YF!r#Lk(q;_Z1nc`acRstW=H)qB(Q=J zf~EkZ>n@WvuDwY~R8SIwxB(g=_b*$)YBMsrl}x-3F&D@w0awbWJDs2@Ma-QH1zm^U z-@mcTh1~X7P^flklq4)cA~ozyWcY|>4T=fCoH7p9A^;6nSpEPS=B^Nzi&ZrfZAcM-&Rifb(9qJN zCMSOdNdibqQg;)=;t+vY1=L8qAf6hx{R^Af<1aGt3fv$%ZObJfeCOxq2R2~BeH$v2 zJ^-818Az8YHSBm$qlESt?9ZRa%%hISUA0 z5Ru2s;R6~iR3f61ldFMR1qI1QT6*{OFYx3*By5O>;_(xpDfS@9*(JR&YJp9-ATUwu za122L0ht1D4tg^ge|~X+@*jw+K0yC;V?iu_K3Y~;JSN_Q-`W&i8tL-HCtKxzxmQj zjv<{iNA_Kt1$*w2=YVRlE?S(iq|j#adm?S^no||}N`;LL7D(`v z5I!&w;NwBxsScD~7a){{>t@3CFJ#H^=N9hA($bB<8-&yo)zp6mJY9N0JJcqRL8BiW zRP?AVxH{gtrcOEkGuP`6@;s z8zL=u(YY;WX5jfLWII4n_tZ<}3cqxZQTZ5f#rNT8h4R-q9{aD?9FLfyAY$#9s$E-v zQr1VIKQ%eW>%0pb<)vb$PDaMYFCTOINjeG8T{W8!UO<@BS$215itB7~@vA>F2>=z> zV8yy?ou1X}(Ff&2Ml|$VT1j5M4QQR6rn~4c0)kbn+h1h%ctI1AmI&u@Bfv`K`}Mg` z-~rH=$FXu_LM`T#0XTO4K|XNkah6|P>()dl;Pj%SMw(OF>)(?q%NGSrK4OpnBAer6 zzRRRxb+|HD>$&y+=#KpR^DwdNJ9D~q)lymgR7ht^hC)@@ZJw zU0@Oek?zLseI0x?gq6G!ilws)FDHKgCR@&(5DE%PtKR(j{Khx08i%oOxSZr4J}8UQ zT@QVGM)2f~=Re~fG4j7%0`H!^hvQUB#I5vwK8-#ScdFe}TvxnMTVz3m<_f;G0zwp& zXuyq!BD4j5FO*Qvcin2hG-pHS-Zjr85D_?mzp_FsES}}5fZN@lzia!)ux$>yMgFYM z@OYn6z`X#q5cJ-Fkwho-5C@0>^e?{%*X`b+hnMRismj-7xi!Y%k&t4&;*+WKc@J^N zA)UBHUai5^j=>aM8NCL>hF2SSKri`OC4A<0<=aDg{_GqaGoxxbRU-9~PAgsHznYI$ z$>;(sR+gfd@)i7)9P!62I-8-L`|?bd^!yN$<@dNlB&T-!4<096>=7JU72e)-Z%Vb}yQ#+%%`T z7+OxBtXwqp8^xjaQ#^J5E@m26M$LdH5ju;}U}g;Dogf+XR)@aR{Nf@A zPx@6^;mZ8{JYtQ0DhNu6UKoPzB=k>hLF7lB=L3SXyx|bpyz=8CT?j@cG1bwADF)&^Ie5qji7>|AlKxIhr_v?oU!!ZVU z>HIHjL0_)0IZ!CbpJ9Q1he56GS=lq(qpbXCSni0N>o{y>?DgKv8*C2^-VLF(q8A?* zvQEH%F)g`=tCr%UeN9j z3zpEh{+8FXv!eEDzp}*)+~ZogxCH;R+h`}bSiTWx=#8x?(7M2sJrL=MEQ-A&66Ls+ zlAL8rl}ksVK1S=aeA>$g_S)}yz#-Ej3Gg*kKuFWoorF4`k&)4ZiHE4;Qf-1CGxf0k z=|%3avR+RY=h$-+M@L0L1I%L z--aE;lUR_+7nCNG(lDdT)m*Fc@K<56<`k`tH2P~Y&WlEz)Thr%aD6vLwYD1AVcVg$ zFuv2N#jstc4bn&{puR0Yq)JFsKpqYkD?!t*p`i!%>L4PistSldh)2BneuEj#s0tA*IhfdXJY0QS>kFD;AnW!f^Xu2=IRlgg0jsN)@g0{@P|Jez z0(epvAh}P2tF`-uYv&I4aDy*%ageI0;(hw`wp{@p?Ehkei&dj7`C-_bTixg85U>_b z3-(XBc}yZspOfih#=W(+ddSc3_w=F4i)*2wq9~LUcUzl^0}o(d;r|vXbueminX6T+ zskje+&zjpf?{f?WfgnBhl^XTI55lRB$@{XD|C0aj4mMHz5%=O={z*!=wtEGEnk4?F zsI%GXq{^FaWi2f&u&~$CLe`vwF;I+=EC8GYPA9wrYHd+8cmCF_p=-?~;I2R!x22^8 zT8>G(K!-tjOENrq{Mfio1*ir2gYRTK-N&6L+L{j%9=pDXmCO;zA%U;Exahd)p^$x* ziZ0X5@2Y=e>x|#Gj<@+A3PbY+j=T5`lxcnsZWCa88Qns{KMKKl+nF9Q-Dl2Hmh?Pd zJ~V)eyFS=JZOi}9i+R&UaceL22bO!7VmBh(q?HVr4)eW3c7J3?A&zN_Vm7^ zOR}c%A3G~2NsNxQVoC#LeP>yxjbrlCMc+> z@zafr5To~5S<0Nh*Kqy^fn)dUQKr8yATYp3h+@EH1f=A)s4~5rtgQWU>QeyrUlOGY zYx}`Vafsa{Bx}IEmH!F1oca=KTJTowpva@Q6K&@BX$o8)U)nJTQHtB-!o&CRrW>b&N;Rq0yEO<-<0%g?t(P5t@~ znijxI^%^nd?J=JRkQ$6PiUJ#8o7$WIv3Oj&CSEyCPPq~sve&k7wu>+BE9thgy8Tax z41dV7Sxa*(j%?g;Mw8vuk#}Ar`-s8 zx1&*Vo~y*dN}A4jEe>W5C0QyU+>si)((TuSIMj)L$QkC3!BaG|GCfz`e!v(zd=z(Q z{6`W!p~r*7jLvsj7$}~)br08KIYo%HP+;>$z6 zh`DhV#rHvO<-f)nIkS*&_FM$~l>|dQ+u?R$u2tK>=c>s+2F}yk+FGcbk)mSoh_x+Z zYa5V-*-ik;Zl;1^59Qe&5Ml2``7stVbL>9CT44Fek9K!{oyokcIL<}?cht8az3{#*d`twhYSL#0yQ-rXCuyT`rm@+O=0 zGz-2vyBZM+dwyvcA^ka`=|Ar{es;H`AAE#kjDws-ZiV|sVfx-Fa?+z7Qk7SU!&US> zD||-4gpPfx-qklgTiUys`JNlEG&o_|qt>C;?d(b)AosAjvAOe%S9e+#D!a$iL`YCH zL^!3&iCv3OnpBM_#YRn#--L5bR8;#;v^>*wP~HSHuIG6dR6U+VcDqohy5eFX&+|&p zj|6nD0ZEfd9uzst$7q3yJ_8Y}9Y&GC5U|&PH$owY3nif$S#!mIv z3yXXPRIT;_+q8p$IDPBVT!l6Ts@}GhB4BRYE<-dRXU@UER-kpEG36YBukNe_*7ry!<5*dY;mM-~W=^`>$U?l@PlK22^!PF!iYm zWxp5lxod74WIWZ`7q?vjW6VGdY}mxd*h9fGjN<8Bu?iTdEe@2o3?p!Inz+qrpj?OP z18b5jxN@o07p8o3FfCZtLYO8@a%F7BE$L&XKG<3@$z!4bzTLBQ2_>mBz3*m_kUOvf z+*_i+H%#*3twtpy(^|l;TiX{#Xo%irsn6Lc|7BV-$cCP2T${0hfkDAdHO}}ze}5fj z&i4~IQNcCAl9VeB1~>eLXtY)B;q6`HxNTTh%We6PEbeOBRX)xp)Vo<_m1X3rVE0=Y zm`}#cdm-2GEPTAL1)mz;;~>Ve(#ec~mB5&JJIb-0n>}Rn_K3;4HsXHySznMF%`DaR z&8t_P1w>! z<1D&43#WS3r5jKn_xFm_Fl5<*UmVAqcxf8LTghsx+@85(%(mt;>?V{t z6&BUziPyAKTJnP|i)p*k5)RC1JAH0`bK+hFN1^{yp>e-HKF_<7h81$<^aJ+A>3~N)L`GqUtBAqt zJ#+6nmXdS^$-B|_n>%$V65H#2j8{VU9<+XT5iK9v*muJ~`TSP=eM}B5lsA_zjWua< zz0og>M~IgtgXaVbZb|`}GGk?~77r~aIoSKdiuGDt46Z^qUQZmJLbB@MH;<=$dnk0Q({=BD| z&r3ht_kS#(`CQsH7NTiP`|SF|gL>S{=olP%bP|Q7gh_3eduPy6Arym8;;rP~YqSnw zl{Z7ri)H6q`-){Tar_bo^YlxetN&mnuu-Y)bwih#TWq} z-sg_no~FaeQa(+F^6msT0YN{04mY!2255!LU3@Mb^CCM*%$GLV9^4*RM3C+t$J5*Y zWwJbvUbTL8KYgW0WwiX`q+9j=L15=q=l}lcU{a4`a;Yd2UOkXDiAu!Ov}^l)@hv#C zb!dT%-uDNsZMN)^VGvp-7Kf|A64FR1+86zEl&)I!MrbERWoLzToU^JL|1d9Bwmt`G zOcX`XJ@o>CINChpDqf`e*70RH=!@8@K80O|F3exF{KOugc$m_|Wf=U%-qsS|63)(M zlL{(@XerYVRsRkAEG6K1u=qzXaQ}8Dh8Umw^7-KDRF{pwE1Mp73W-*|uHvE_Dyq1w ztZ(1pNy&=UrD2zPlbN#+5_mqo{a@rcD=ZzSr+){(KyBa@p<^Py_Llg;rK-4Gc(yn^ z#U4CJr&9dI6YotXIPs*u2#=>Ho1GSxGY!%Hug0|0$VuRDO^&^hEo~+nc??W9sW1As z0P6{*e0bxou>w_Mob<;JQF&Af126mYZUM3{7E|XG1h1=$|GzINtUpv@|eINI|vZSQ+v%4HR@3o`#;^@~x37wT5 zguhv1VQwG6q_fVF$W}^R>HZ!ejuV;>z2ZNYEd+AhWd`GmEb#Lafki?|2oj+16{Xll7+vn;% z4kBl>YXFJF`eX!a!;Kj&=XM&J-sq)CS9cP7tk#X!-FTy*a9q`U-GaO!NXY7K(gA#} z1;Y*rOBTYUN|2_~k^Lu3c9AehIzda`)Bax1DCSzL!{f!nmX$D#)L=1FCY+ zbp!8Jbr|_GECfB~h>_Z1QS2pEItj;e08?J|!5`qeY2|Nn^fnP0 z#hZpAl5VhGZjt<`e3!L&HK}^)FYrec0v$q(2s|j~*OKYurrK=?KSc*UF2j{cpGKmb z+2y{4$e`y4ixDh6uDzWk>dLJ29xJo?^qne>tRHp5F0UwS+H_KE-ox-f&6 z7$u$kA>yv)^JMAlN2mQo8}fewTemWXYYX@918+7#*lR*#X5_il3cNqLmYk7y|2Ijp zlqnqDE1P=+Jafk?qp)s~8HZJM;8;Aocz`U&2wqmqzOj*OwQ~y?T);M4Z+kOZd4QMY zn;P&kT^t8n(ljdZCo_p~$(PJHD?|Y=qXzAwmn|1Oj2;sn5DtyW|_w8>$%t!SXl_Z zRGuv?#DKTgFi_eF`D$&loK2xnczHc*TRMe1RB^97tTz_Fo$YcgsZ29{l!U%=`Z!l$ z0Dpqa!v_{c`KU*}cd)B)h@7taaaj-*F0GC5F#mgh07cG_hA6SNf+fp~cv0%4Qf35)FZ!_x8;Uo1cb&-+V;8#Hoy=f8 z$G>z>N#yxYX_-XyO3#o)`da74@3dP$ID|cXX-a>Zx*j|EJE8SWBohuwGAf~Um;5hp4$1Dn z^RIFc#AB-JQ$#!h^${8~8O9_A3f~M_MygiIyqFel_-7KVTakw*JerK#irHRyF>!53 zH;4W(sfP{m&?CZUykaKDaqS!y#+teR915{6OMVT)d&9G=-7_Nx38_M>p}`l@tKY<2 zqIBQ>_&MIGP1Q^BrjOud0Ky@i61$ECVPw-|ECBWOsx`Djr+ zL6iKL!<Zu6bakMji%k2j4}&=jw8E+MO%AdF?aj+K1Lq`}c6N_Klz##r1W6$a zO33iMGTTT6jmlKSVrp(Cf(e^^+?Ghz;6}53C3kxGoE-q9hOM>3122{34^WC`vXKoK z7>N5fl3$w%f4g-z7HSpD@&95J=soU6A60Ly>MSML4gA;((p#>hSf!#`t#uNU!rUpC zE}(XN=wDO%(QMT9S#KJ!FES_58Y6gMw7t0ism!!KqTa zfaZSzK54dYaXjf4sp*NyU2S^Kb^H;0vM;;ZywL zAuI55LYIAjQC>2(@og826C4x(S869HY>oAf&ovNc2Lu(bu)CHMMA>BkDqy2RM=4w~$zc zrp89hc#xVLdqvvk&rnIBU{1xyzYU~Txm#1_^OT{kMz^%5%f9I1OwkGY!bKAqeEUmd#LZwh- zqyk1u=$+_1c!7X~^?iX0!ZqY!wxF|`3Epq4jEvyx@c?N`g*rrVvt0}-cBX7EN5t-g zRPeKvjg7xTMP(%=C%Cn^M5XmCvBkFM=H>!WDt9+m<#lhIp{O7Um(w0@6~m;XSa3V=W|~yvaq9%OoGgB!B%lJc4Nj zK}#FO53CTxbU|Thy@O#8$|N%dG$(9VPPBYF%s*{vLe+DLJ9#^aD(ho_o|fXTUt3m| zZ$~x7o$#9N<+69A5^s=R-j6~T+n`?ec5BA<{FfrZC3Z5{jLAt`AMd2~{j@TlZt!oB zqmwm&7KstC(B$RQ{IKxEneQNO@V|ammpR%#uG_x3`{3`laq87DKNN)V9mRzVtWKCZ zx1Z&aEL$G(i-(1G*pY|@_3~kGLNJgEcAN%*@pi3V^~?ZXYI_9 z`OCfA$u_pOML?bfG&p`nSs7_9TjV3a%)$$RlQ(3Q85+)a7=jT!83s7UphTU!DR z;PJY&q9Xo@(QRBtadB~|h@XjqixZT^rRYjLufb^d)d3qB;8wt-hgrRU|7)E+NKx@o zX{=EO7!>@)ShGi^5<%IH?OJ5!x&uH%3;IakZ2>M-)u-eso7Z`zL`8j`u;7E%r-L9k zQj4&CbH`>Puf|Uf)l$8an@g;sg4^DX1-ceGe6ozf_xhp}9^JmD$7n) zfWiE~Ee>$K1hGiaPJs7wvx4IK_1?dJsg!A^Mnz>^ z2rT%XSi4gbbv{$nR;9(~&))nL#f>Y|qhF>83d5eBH8Y}sdAnnN-Yri3hEl&ocMNmR10bMa|c6KiEn0Wl8 zBv+L8mYqZlpC-qZ7g^6oq(tw{@_ZSo=oQt{ke9qW*;{`J_0xE%$Wb-Uy_eyYA!Bo8 z?SFJ9Vl#gbY0blrGhNEBKE(G=UMs4>XLIsXyfrW{{yO*H7{_qsK_BINO1l*;w-64~ z*^fb+>Eqz$=vG4qjU*M;P`PNp-GcZDDD_7~5R;NNS9T+=Ito@+Y%1Y^v`|hQ8XN>p zJWVV793RyafXPBf%*EwC^%u(P5CHj}@*|nTWN&NzCv1n-_QC2Bz~}{nega^pO-({_ zNmdq=HC0qpKwn$|ijFEC;>S^c38z(PV0^7zeW zBCbKVg#A9QNbchj{)Dlz`v@atRW>?5ZyYLj>GJ$Ou}j$eZ0yWmN6A*5k9mhLXynYV<^ygts4+c$0!wHQ9AFm-@ zNS;x`bL`^A_d0!d$73>Q9<&lrybm2<>@eVhdoKlG$iP1X9HNltacQ$*12_AvTZj=D zGXtlA{=RjB3}a>7!HkkOKV23qiZegZFTQ*CETF339gN5Hde&@=WImjWCmto62)_78k+K zxhyYr({=->Y2M&eUq?v9GB;^wvr0>+VU*xw`P9iR?$$Zrpq)^^4_)ZAi;}$h|m4!%w=8H#xV>vhK#u zyT`lU+0IIn|19H}L?QeBnR0_K;g{zo@!v|b*{#zjQWK}>=_jm-pIuXLUpks@Fj5al z?Rg&Xx@&3oHPT-3FuZDh{w+TK5heygOKtU7fhUBnFg%1WQ{>6%Q$_s9cxF*|)r4fW zPlXGQvQ-?ua&i=I^gZ0a^s+FFQMd1V@9TdrH{&BO&4>=hml1QAjDK~ot@O=Nc8F0( z`x1jlLMU%m|w4-W~E5$MAq01Z&UL4uGfFj9&tD!}nL9O$n= z8H1iawE4|x@r@&t^HhL<*3r{b=d|+N#)c!f8IOo4tg0{v=jE+8@48Gu@QCGmVQ%hE z>-)eM0s~(F>CH5O{}1*3f^@P=5ADCedG}opaqeo5`t{4s!?Tct*^>E5e;9>c8SF`ux z!q1z{U3Yd`ui?Y^jS}?h>6smBJK@CUrl`N~ZacYL*7r^Wxe}m{+KpMvi~q&-dRUaM z;Dwtf05Sln3m8+yC}W`Ufxjbwh3V<&&OUQdtn30t9b`uU3=cm5k@6dI95w5r4EvYI1tQ!TADfcj6UGDVJMOl(_dps82se@fj?)al$;9wGsFa0 z`$iEM5bOgJV-wIxAT(xaZtgvI2hMXqiBr|EYQ=)QQ?aC&5B zAr}C5uhqKxDQ{0H6Z2ZiL~h_txp&=){b@iuA$knHkY&lXy->Adi=iXn>9LH&{sgLOO6@B`&3Si!zpFi#F?Gph<1~f*H zUvXO?WjIyR(i#J>Klp>FsjCZG{>sf(j0fKiq3UP24p%`p9VQj*DG>ZU9 zeE1N}_Xf#_gc2=323iEJ*b&ekfiGFAxdBd6AaaOCb$92VF+Q3y`=Sneorp%z4z3yC zfW^ff03Q}SR)nf`$_ZL3%u5WkzPZ{?uHTK1W?^AWWnrB~c0-3iB`ohhE$}E3&f9Bn zOCzkn=oT25bEA5Xff9}*B4TH{cm20G21?nR=QH9(e^SuoOyIIs-07ECa99`=qpI&O zyK$2v|5PvTih#zKDKi~SJH>B;B9jz<4E8@8s*>2QevTy2cPgkp$QadYOl7ZV4oMU_ zxe?muc0z|ijZ^W~ZMfmsP~jv(vF3`!&0vck&XMRn)#Z**D%PTYU){9J%@-n@vY|1#Xk z09zLV@E$L8%d58%txv#XxH&COBQCszj_#3*dC74vaw{_=a5%J|o5F~odh z^k%^DuFsnI?w&vbHwkq7OvkMo*z0c=Qrf>KEO9=v!_<`I#u|5dOti5R`vuTH z&-?g6%{y)TqQJph=#2_9t;ay2(ick-!`MU|DTvZnkcyvzE&7I<9_4B`Klrit zj(qZb)iQx&a&<<>^hZb-#=pO<8gkT!O-ftc=_{$>rNL`qBFh?Xf*(tF?Yx-pH*J3Q ze`WaMi+Z5OZrf3@l56UlOgh>5%o}86MLN}RmIGU1z6UwuU41W?vIg=XAnIKQPB$=m zpmCla6NC6I-e$Zs30@PxY<2?;A0TP~UBDCm^whd@`dzU5Uj>PiUatGU4;ygGq@+HS zB){3Cb`z^TuKOE>%kMw!SHmu1IHi?+20lFi&@Tqz(=)(ygO7<&wd_xQP|Di_z8Y*4 zG$jMBvv9~2U4n@}sKv&|qxtC&TR)dzqP;C3ZV`PhVly!+3(95GUDGS>4(Ut;n6%OSI>G?+Nuh zp2-rQX6OlPrJTz3Qjau`n8$8-Sw5am5OKd{?`=dNXXF%fv%R8Oy)N_Yy$L;NVhCmX zd(sHciG2VVCL%ED`XFZjJd+LfslS@HVZP@@}{e zEEt!6%eU$V^R3d|J~fZn*nb5)9gsUNp%A#`?iv3i2Dw8Ol~OQz0OBZEHGu;JSkenu zhhFV8<`DxDJ{(b-E5SKdLs(q36ru*c2hmC@Vs06>2K9t(k!|U@;)&z;M@DRTffIsKAO9h+Sbzu(GC%tu-{TI5c(>Q02X9)My);BJKluF} zF{|x}%R`>2Vxd4;07TP#8K7(u-bxZA0|f^7rT>6Wp{b?y4_4c%)9K5Vx8dT)kFr-o zPspgPfV%SOL7?{E&Dev+8M>$lG9q3=5ia3B>j4Zp#!c1~_1xaq2qHfyY5rHeXx!IE ze?x-ZTz6}(iXQC*U_U1pLn;!89s(xhIw&9XH8mN5_|1K-Av2R(Z&E0E>0N|QU@^hS z{<-@4+;>ZwIUftOrkvqF$iJ~EphN|Koqa>K#Vkfn9uEdoFkJ+wt#IUI4s0*D9PDW5 zg`INi>dsLe@6cK@z}q?+F-zYVuiS-Q|H{R2GgGgnxkutJr7-fV)H<>1Vfb?m4K$|% zLQNx;Gk%P9E{;gf}jy}paO4UfwC(6{W*|kK+_h~DmWnA0OKy$mY67@ z3SEVQBk+|h|K>-;$CsP;Wr02md?*6U#4ROHxDT6b*|@lHP#8NAPxc)t9yq$z-CL1jKn}u@R|~c8mb`ye@!|mPD^_37wY}7;X8OE5!iR! zSJ|j-R*s3RFy3#XE2ce%;QhbmocpTQ9@4vh_zRe5g3@CpB?Q(DRP9m+?>n_nGnt8^ z@u!DCasrR5Q+Iy`IHW^yw6C%gY6x>WjOWy*FXHbcO8hpchD8aABEVZJ1Z9lh8lmgN z!Ylh_(UU;q0frXn5}BB^vOOIfLLy;Br_y?h3p-Yy|Q;_q~rlx~fYY=X<`!b5!r`y`VP;wMss$cD5He zT?2WP%iJ5u<81_=VhjxLEuSr`AmCgFA?5@SbAK6sST;Q0p(xOVF`Y(xNrJ==aJ5zn zf4MJGWG5;C!5*k<@V3aOOebtJh%sJcc8#|iA{qU7zzdT zK@=+3SOP=?flC6^LS;rGG57=M9k8*o9s&LBv)VF@`L?R$p2I_ z6a#JtuB@ym6IhpMOTfXeW#Dy7LL#Meu_sXo&CWLCd)+z@DcD5nPx#aM9A=$CSh}dK z2yG!gO1sw633@#8%%E?PuAF59vO)E!b7#&@X(S|gOFdV8F8T=B+DI21wk6p_R?uf9 z29mcqeFsuh0fFHvT;8<1)3YKAQb3g{gfd-Dj*}*4)Qc;Hf}hRvDtv5HsRUhgkIH&m zQw=Svi^0!HqEZ7JwpG)A+X^mcgC|c)oEH@m>?LbIepv>Zs<)>n*5%7@ff)r)jjm7R zOe{SZK}iQmKhf1)nKX9|L>%2Q>9tL7&Mla(ChwC$YK4J1==cP;G(?T-3Y1W{!9c-6 zL%Y2dj7@JTzdoBMlpwHp4G|WU3b_nwN6+!#+B}2c0c>gi0nI!lI5-0Mnm`^0)ej65 zh+e@!xq%=;BMlk!AjIJT36=fe=;+_g&CNB$#dpzAe?btsnaT%!TsRi6gdpvk2a7-sAqLb#6Wh5)L z+10KBwgsvYtb&X(8BXqcDB2_g-v_Uy7#0F_>z2O#wm&x1Cw*;M@a@A!Q$-CGNYP$G z34<2khYwmc;+qY6pT2?Ri4jOKMS6O9ZF|O|ZM^e;>L}0?O1?Zp?n7hv+%cM}e7)XAA}2 zyql>_a&~5BkTE5t_sN%Y3W|)3$JPQhU!!SAWe#@Ij9>49v6S6hbK!-{{=ot8j$#$s zuUri*KIDXg60A13W!1SpfajY;>dfK;&=4d<@p$e0Rwh`PpNBz%(jOsb;1K6bS<1sf zZ3865@!U7-?KL09{x5%VT)s<;__1;kP{K*j+1(8_nVeFZohyIej9fmOe{%&~io#mw z@66vx66~ovd>ADO;r48Q(}HcVU2Fcd<|dqR<`q2z{Fjxd#P^jJKK^dle;GvH|ELJp zd;arnA{?}S3|vPLyGiJjTa)?rS@I*0nmU4}6F4X}I-LT~+3#~X;U+LRO-d@cM`Snk zgzjy&hY~-OlvMQgmb1v&zz%ub^12xn0PEtNe3}2*G4RHf3p9Z|`JW}Q;l-tjlgf3U zX6~}c>R=kjR7h-(kB(?1d`Atg=Kj~*QAzQ&xh0O*lHzwT)`fI+fdIqp6wmpTkorWh zXi63H;$vq1{m~+2`S&NNQ~63yl@z$}Z=#?Rj|wv_GCdgLy@tpZXUR=;~Z5Lxj}t?nbRLom}&mN4DJ?CqkYP&}<#3 z7g=;CxFNtq&C^O2VQZqvr^EV>y7Bv(i(=}S&=f1ttLFYFg z1j)cO3W^snxXC=mfy358J_ePJAlM$34i0e`D3CrRmF2~A*_aeTHG&o>Cpm6N3)-X5 zRdj0R;@qQLcWQJLf-eRM5Y2$t?BGFffqs9e0K~_E4^TSxpJ;ww zzhzf%FPd3>aW-=yDk(ye30US>lARK96{~A&A3-FVH!P0S zn1ty*_C2ox{m_H}lwu@(Hchu>vT=)TL)GaYxFQJuRQ^6P#5@-jbY9DoDl)G$x74Y? z{={uqZUCO_m=P@0hRas#I80yQ#ciG(?ppytc6R<1Xb$ijJ`gsr&w==oN}?bqXpVx4!o7PZAPNQw z3XYKDgPhGeL^vqH#DadyFUrZ+E~3*22+?;{p-yQVT1Og*gXr2AWNSd?PZDmTnwk?_ z(45<-*=&}A9i~hmYVsZDwM@Dyt7QV1%^t!122cqM6d07`Xn;lPJxIBrtHeRxX)6fI zOo)W*U!tVjeyK$jEpg~S2eX$zPTefqIGgIBtQ?`i1{zb&{{H8{m*)qb?i|t*a@ZG1 ziTX5BYTz$I+1^B9s8KrRSMkc}o^P;WYk??>$boC;Amv||#XnIsWAjz5!DE(h3jb)N zI*M%9EgfdbW<9R#=v@(V8u|`ZUf>-Ig6QM3e=nwA^mx9UdvgV}n5-a~yEqoE#<=%4UaZ@pHRQ$L(-t;AJFDk^lC; zC3|amTXK)#D+-q9Une-0KnEkVB=v%LmOC%_lJTSbmPBW@G+%H@vp9m98P+0Ty0qNSF$5# zmSl9Nm4%y|rU#k50UBkME-zb)U%49UM{oa&T52sjTK!p+JO!@dZ{N~R{Ip)*Qb%Xh zya99ii$s#d4%iJM4nwsD_Vj$kfMEb-3n*u!wQnH9sf79z-a`-vJzgvA)#msGQl=@s zJCE84gs`aaEE8Z~Y(XeL{Oi{@Fk^?Z0V4AJ(SQ^w_=@tswVV9=Z|1_gt&Ljp=o1TT zcB&f@lXBW%$@xE(RKGQjKM0|_h<)!{4|@RDZI+VYF@3u1NP`(4e|%Q|E}q=9t3dr_ z0CD3E3==^&&;i`#eh=t#4Q|)zk+-Nhk{S9Sb-C_Oqi$tSS+u@CvVW=Zk01O8``kzI zPV85IRQQlZ>~V#n{jKA&H7X#Xz9AZf zaFXs(G+Ioy0R#y^TM#rFc|drjYm8GGy9k7QEk-j+}nqCZT#j)Bk#Y~gl zgkjL=*yrLBf6AQW(Ws1++BzLrUkvNT>OKFxnMLn|y%AMMy_Aes`J!3${96>|qj?Wb zms@G{Y+bGcd0welWdTN_^h2%|q_w;64DJf}S_@=VPrY>HwbAX9 z!ydz}rJ|C>Y7Olj2*hdgaYGmnTTDuzm+cExHMUr5kmO`XvE=(VX#4DtJbJf_ipKjuGFd z|6)rg0w*W(i**z>3ZB1*U5*!l!SN_{PiFP{_44b+kB;5(uv_3{he`;jZVEzH1=Wpo z>p1zP|61;Cplbv7EYusyJ-1ETSHf`yM*8^u;9P?jhJc!wv&a#BMj6x5F3VgfI&9(1WJ5(ee^`=sId5(Tj8G)WX?MTV{tlV1Z0#VY-!Fi|2Hjd(5T%4J{_!*K4V=O*4j-8#c_K3AyPeG)j>JKl z9QTi@l9fLsCi;M6M+dMWfb0%cXOL25VP^i`-k!lbEI*UhZ^>m?53aU=vB(JvyH=uo z3VPaL(+aY_huasOtX$u;)LX_Ne%|_|Xvf*!+bimn?mjQG4(ST5+}T^qFzF`f<c9Zv zA5h4l|LaX5RV{00w+@|75cq)y;DQkgzzGDwXGjF7culavhnqe}OW?G&*08b7Tm091 zSGYAYH6`et&vmC`>&H!RLc+w-#9-HV{h_IK&Bp}>I(rL$f8NLm39)O&va`+=T}>!?nx-Y%4!LRJru*CbK-aUi)G+4^CJV8 zdT1j-qpiVt@tPD7{9iOWLDIiAGO{=MB=2f@be4vb#`8(O%UWsk+1A1Wct$wETP4Rw zdZ&O(c#pSVaTJp9K-`wT?jnrn_c=oKwhgq9|12#TR=X~PK?4|T0s;hq=Po;TH|(NV=FL3C?Qw$M>Ja z>)R^>vV}TRMV<#IC_Ba4Q0~cSxy`A|lFU-i>%wOX>X9iX$k%pb{!SGNzQBk#f^xDS z({FUYBh;}fo#|gV{y2B{w$<%Ay^}&XpoB3V4{8aM(1B(QaPeb zwx6U74zv=Jj4|&ly>cbeOWv`vBC{0aJKTok`c*f05}gPM^?YlY?sV|D=M&JVssG@9 zIf^r?&RRe-W=nhnj z*`}nV!0f>T(FA}tea{;&T>ds#CV&%75P^I&#ErBd39qJx41h7n+1XjmVd#PK4{P^w zz!5o;l_%*^_i+R-+U&dGMQs)ML`E%6i%;RKdS7(cb0{efidVtr5Lcs-#(re88P3qQGE^8~Mot;-&MLR{$u9K1> z+mA@#Y#7xZEH-Wn#c`2&<@JLImn~+{CLtjd4#w8jS{k7vK(?%`jtTetSTwWEza#id z*!eRP>)%2F<>6P=&%Civp5tQc%TBMHpm8t|Ui9U#mUhosAnVwowKrP@^BGo zvEyF51~(C$YmT%4XNTejn4iCc10A01&HgITQmqAZevg3~Me#PhkGqjFDnu?J@If z_Sne*H3O?VzP&b9Owi3W`1aQlJ+zg=rmkf5HDIGZ6~`XtE2N#Av4_y z+?;+aI>!?NrW!!}$GRO@oQisFoa#I8*F~8yP$_~cRXQ8-Od;CApQO9tLK(J(wvD(r zC@_tTjHr`d29_`)bMV)4X<-2*7n7!}KZgT(MYo`^urRozudtv19~H1rSyAEP=vWNW z5}k{H$9AsrEuNxn%0K^qU;;r|r;-z$0`2FwvWlaMVDVm?-e2{lG-oFHd2#TUwkin^ z{}v|Zqjxyn{%dc#gNH{-pRz?LwBBJ~U?bI!MZ(neI@7G~6Dg@ip*JYC!cVh5@s#Q+ z8cfv|-n90#8w&diK5rZ35uLkc-4%;7ER-aW>U}P9p?w8a6m4ByYaU7zisbco|1q=u30@tIXI4Y$m6u5{lFOO{?AC*`Zwy{qFa7Zb99{7ptrOj$S zbz)}|e)@FsrvgaoB&PEqsDP}j?vauC1=IcQlH+4tKfeOUi77wFVZ-57o&Ei_ab5Rk zbT#!U_!V>5k4-@kpW6mTiuDHxr9Ad!9Wy>Yp1y&TyhPzI?U($euJ>adBi{P-m9+te zTk2yfXN8NQF1&c^00c5Huy44d_w*^?n9)T;VEIc*N;)_o4&2<_hK7dltAm3>C2u@; zihOi_QIS^O@c#ZjK0dy6c{MBt*rE0H1{wXp*r7e#-oENcM2mj9%uAD}4(QXKThZB+ zuz7|#D~r56OS{h3NX>Y+Q@TS{GMs^+PaNknp_PqI9!PduT7rnQdfxCBcQxR%>g(xK zkgFKr6WupBIN06&m@O92qyHhTS2oZOXz@->PWGWjLb3*z6A}~kbaes6ZEBhUQa_gb z(Dj37ix{xVhrb<#0S5^WkOrm+PS4J+=t1M&@Ido`CC=Mm zOY0j6iLXK0!sTxNq>?57E8Xs2s}9dFiiq1IGa zH{0CtJOelDRaMah=Pft%Dg#KMx z5>kzs^ZRQ2&D*`sG?-%{wy8AF^fq%>6l6n8XGV(##GgINplu*1*i3mfMoCdoQCXEac(qn-k)!L^#;J>w@42&G)g400=5I_ywAfdVJ}(1Rm5G zF5N|1 zE%FH=){=PgYgY7;e;g@a1l+)3BuCVR3BH?zwVKSjO5kCdY%{Q^HJ`0B4Dg%xIz;t{ zPS}FVhHV=MIjw*8$O8Xct8$L8OW2s5wrzXfahl<)t{lk`&u4yFU+`o+laIIwqVjzWS}5t^MKF>ZmK9I@S`Z}?F^9^AsR?2Q8c5YJ^1b0%m;c$$FjJ{Mp)!du1YBf+yiNZL~q zSYz!2i9;h==+uM5|B|!^1wBO)eVe6}e~M==gVT4#Pp} z+r8nNgoMx_ef5Pmo<>hsTbEO@$YR-*qkuewMwCu5tHn(;t|=e}VOEZgZwhE(DiT+? zQAH->B?j{-b3=jS3p2@hJ<~hY8^pI_)rw2scW($KrxPwuFZYw)F5i#cJ4#;iF!c7T zsxsNV87v1^f>{)>_O`HV#RHc2k71>sF@VD+@T{U=QQWHQ4T#cJYba+3fCk#WGoT{~PMck>H}B#9K{sIik-qFO#F_;!NvY9{*5vVGUWf*f%G} zlJcewgVMxpe&WYLr_DDEo?LDC9IAzAwB#7078I%o1n}vs>8Y(E-tfr2(w6r2mW~L^ zNZ?Louy~_qSWf@=T#Cr-e)U8`6xM?yZNIg*S{yw?ZuRelEQ({yL^T94cpTDiI4*F-Ecjf0^ye@I%mH>(|&0; z{$jlGZ6l{V{#J)BRi(4n+0L!Uiq(r(;bR_CT+3J)bvxAW#UwC`jdA9ByW+@*!^e|W z8OnI3c00($h_{hdeW*pr@3iR*^cQ_4_0&uMJnD{_8h6B!iaO7o@}|F*lAfflY|0;Q z{7=_!eVLdO=QZPx`+@T}0`agO%c%1fRmL0QSx9QSfY*>BYlrG?ACHT6rHEKFEwMtd zMBmvaz5$J(kKOs_**n(lxR|BOUys zi;v1{wL8|?&D{q+13e{76(6c8C-T;5G=#J81K;SGw{;7@yrLIq?20E85|Y^BmoJel z*(;jrHZfUIkf$qTAXKkK9l?k*&`?g(nmc<(!7CFRa05}QB<=J+RQX&(pUXSMH9RE3 zTE0X|L2Ra4OlT1F0pV6CjK+8qN)q(W_@F7z<5!O{YWKy=6{>+Zy?GLHuZdxnY}U%P z*Qx$vR)ktUVWj#S&-9#}Dp%nxqkQUrWAe~j-|#1Uxt0OvAF@Qp7}IsyljcZNfXb=F z*v+Y3oqY?daefOSm~b;j$0Tg8idHfE=kn-`p~oKK4rF{u0iQMy*PTrYCfCFtz9x$65}E$YfuPZLT0O=y<&G7_A~xIW{@}cH3Ev z-kcyNaj_}RD3B>>N*iVq<42$lTP0a>AOT0FdQ(BM5s1R14ionHzdD1QCHN9E638fy z1xrn8<1MzUK*ce8QBg!C?Ynn;*gQ{@*sf)OaKXWpumsM1#_pH0H-er=siAXB$(~QMb z{B!uJn52y8?f5D|^yuBn`usac?81^Ru9piXa0_ zuId8}5~-+@SFlY0h1iI{iB;?J(DhH<_@TL#x!d|9>ep?224gT~w^Y1hJk*<>QaWNH zC{&>sxj6yutp|(dUN}^NTsj~`9gokiLMzxuN$?ouWC-*4*LU=(Pn)OZ@VG1M=%?$LpsLH2>+H zNfsT8HInqzsSt4M%*KXABY7G-?-wVX#S5>X{EzVi!Wi$>yoeV40P;pD?H5s%J;~e6|rW zop#VZVW+3InI)Y(dWzCnhJsF|+nJb{AAY5bD;ge=pl3#Yk_IWa;tU|@<4OLI(P@XF z{^CgXxbtpzZ@#Ve7$>IsW@=q%MBOzT&a60W{w5+v>ff7G(eKlo)ZtJOf6<&R>YiMe z^3m^+fnW`E4fqV<)3@Z zKR~*Se2>%SQ6$UyS#ss;8!huDj@gKx_;Y_8i8-B3)X}RVw}!KWJze&N*ptL#S$4*0dnI7J>FFo^*A#6bcw3S3Q8o|Xy0v|QIO-T>}@hN%uJ9*YX z)Fq78MLvBlkS#z5uj(Wn1^AWSivx8#XZMf8yxq^(E}nkC`e0z|>!h>rwf-)uBY13O zY(FAHyR&MM?iUp1j!yJWlcx+mn*vu5ov|bU4f*XuC>DdR0QAzqkE01O0vCF;aw#7~ zD`Dv%Z;Fnu8Nlc{kZ~4G`gg9PB?#$V0tR`S>W`&ekZ6);c7}?Qhlhuo8>I8}^z>>u z?|YXH4{#qwy^#Hncx4z}5n$LS!~Y^g`^oanhR6f?!&%f76dHnSvJh^fFj-oZR`J(W z6f_199!f~!fSyi?CzyyYfurII?^=W|4lrP#v`CjFxVjA6~B^kPW~k%f8bN11}$+=&`spHzdC&u!T6?v+(ljZIG3O`Q}x zNP_)NxFMOpt3zQGrmt73sr)l}Dm|hfed5}a_5U-aqtp6HrtF7zVKPnFkgzqD0TtGqsKZz0#@{+t^>bDOo?f9v|(B-U@NQ`@G{i+T}oGR<9 zW!#i8M_LN#ajRg2=r2)D&^maI-JO_9xv(!Q^5Y}zv(x3{P2OM z2SPz(s6Sr$-U!E{;%_~FG0hsr*Fe1|J)92A)SySa@fe4SL<##!OcRqCRFKUYCtKXP zf{kI8A@Nnq$mr5mv=ZU{V9x=U=|C zzyUBmZmm-62)#(KHLTRPmzS6SufSI7FuAUVCl%FGuJA zr;C%DTMz?aMirPzw6wGUpE~;QH5}n4Mb>uDpK3a@Lrg=Kl*?u>dpYZCv|=|F!Yn4qnHDhM?%wH(&48#p%r zAS5a(3Z+zd#KdYN8xRObOIhFK{?Yj0tVkv%OI5VSa%fJL71qoO*I6+ZYt_$t4Op0z znDLhCd-vQnzf&z#JF14a!d9oIp_!{Z3=YN?#;z|Zd6Ll&-ZSMj-$6_S3T8a78tXLzJXvdFeG`Aw6@aA zM8``vvEP}CjoVRB@Dy^`fkO~oQ+&~Jn`cY&+RkpNrzc*Q(x?D6eqMcw`qS2&>{$&B zM|SoqGBVoah|8$iU8|}jTG1g|dV2ywAvu|N+4TB>g0RWQ0$JYOQ377gwIZA@aR8=G z(@#RC((O?W{u7umzJdERv^FK!+L%D3g)3MnF|l-F)t6M3jFc3(lkhyN_thyHDb>yg zlGOjyP=5T7|EMI$Q<$EwX#*VxaOMUc!!=x7aAAg$ffNxmK%jXBgupk@;iD`fl}c(2 z)S1N#T$o>w(~p?66G8%+Y~$^s-3KQ29ZqykHzu3R_&``dfS*5m*t+rwR3;4#i!}4- zii{w)&dSPy3K>9m!9YNRt$gXqsLF?=;N6yf_bsCi^21mW3m5B(*Ob(Z(zACpQNxTxgRO>Q^Bqd><#Lrhtw6 z5tc9b#SjzoQf>?l@B?jLM^)7k_)GwOyhD18wjDq{2~klzKv)82+H?astmUtp;`J9l zPuSUijs3V8$ix%ym^*lRRO z8+7&b0PzAU_--B^4B!9+a6ss(LoX+3O7u2h6+1dRYrtF(MiQ80;0z1ycWuy%1$HDH zS{4-qQpX=X&Su1aJBK!l)~mnar7XS_+>OFR(KITXO62)o%DE5qZwF5uhnf3`pZv(* zeecnmQ+<6XOtS|MF!Q^yZ4O{S;%U|~_@MX?EfWLpg3Sj`CG7mv^QT^OW2NcPN-Zz9 zB@e$!C`8C$|0et{K7&0i-{-Se=AS(*`M(e6o#ts>)+)rgcI}AeI@deZjE7VYBY)k# zwTyYGuQ#)Zbs-i3P0?kqxqpwwAF;1!LA@erD%~LSxfQ!G*<$UEv$EH%imyp}2CX@P z6>W9425<`zQIVQmy~gtL8f`@|9B9F3~kS*sTpTb1Ts^ULeOn< zS}-v&>EkIN0J;)zpv^2SmKGPEkO}~o7<>v3r3B8l9`ha2GX- zVnpGEtm%F$lU^B_zkPeLB!AS?$u~gdzGU@7xnqHZFCe0w=b)aW;fSd`bQ|Xe505Go zi_G|{^a(814#!q3S-GaU!h` zQ?0qi^^xO2J}bA1CEwinIe9z{gP#UBX?p61-DJ|#)8?gc1?9!sIIqs3&d)=B1w{gk zPbf1@^56 zDO|+wlv|b)@7iT04FfkvG(#*cjR_GI^#=SVxQ*=X@2?lS6L@03oCE9+z)n`d1`#LF z6UHD7oHH388xjA3U$50d~8ls`AwX1cDEMBCc3bVJr zUo+_+kdtS?8VSuUEQGGFLgrV1x>i?@L!2zUvAO9$7&SLHmxiRJ`TgF&hUWeK!O>Xu z(+s(10ISKY`dCRPg7@graeb-djduxP+3v_GjB;OX&b%b7YtrSXshUGEZfi;+bZagTC zF-ke>9=K$IGZX+tfrTLn=9j=X2H(VB#+O&RLJp2QfbIch;{UbWAb-)P7v@(z%(gG{ zbfu&ZZe|)#c|!UWHYmV%2X7*^G$w+HR|8>-gjI90u(at+MY3|azxmPxjeB4isw5Gu zy5>Ve(ArwhI``KM1HR?j3|xrqk&1kGZ!PtP^WZVxT4q3MIXNG9E~cM9%;KGIOWwE# z&^W96+_nalpRAe!J~of6mvIy<@XM3QWzMq6Ct+3OEuW@+BDk}F^dGYJZfQ2^edPMc z|H=5QQRcxsTt#vTH#WyefLw`OoC7xrNVI^qww`Ma0$Lzt?)-7^Szj|>V)eI4o8Ejx{yHkxiVSwe=VDs zs@Qa%0f_|{y+0B&r@lfxgEV4Yl~vKu@+-*ytpR0lHes^Z$)AFnG^l`1$2lzdqjS@l z$T>BJ2%O-6G-SK`Sm=!NJR-H)dGRN}f_rI%?3F(9Eds=IYj@R%?(XLa(6k3)268y% z0c4C=Ed!vH5v=@>J-!E;M;}J30CWf4Bbdb3uK^3ABrhKt9{vDVi}A-4K*8Xv4`qf%1Nf831B*RLXZth(k0-5bFFb_1YEK}qQp zoYGWpF@2VV(@Fx^4Zv9ilzp>#DR2}CxLv@}0K0`PA7`M>R!|R^0R{&2`l%qmCMPE+ zc`#}U4p=z&`0Irkcn6i*x<5~L6gNRWLO*G{d}Js?ZZ&!8#`lh&omDfw3vRzd6}994 z?T^s3y&VMVI`Z;^aC<8BR4=R2!)N#zLuRI5WWy)O(<)SizWo;>`CLs3ZY39uUSrcYSv_=HS*-$}2 z0ZjJ1;WK^x$_IR5084{cD1hiPnuc+ZfoJm3VsM55TMjg3z~u1~CgyFW_$V-Uq@XyA z>J{@`Nbe4|8~j1BDU3ujA`20A*?Wo6%<=2>T|Y1|*SqHIhe zOP-pmUwCu$Cbf0*W1NNgx1RUos@;+HTj6^j1pI9**9zTIcmhf&mBg@hf}pvsX%GWen-Spy{IeYJw8(ek#yv1 zhk?D6jiY!p67vdG!HF%eZ_J+}Jy|&F5{AbjiAMK|O5}aOg<8MPdmlW;B#ve=HE^(& zZ6MnMQ#U|)Hkp(_U=D1j*Wgh6Nsk7`zZ24h!}SS|zkN&@{lWm@1pKF~F;|Dtn{c^^ z%6#2L1}ZDSjwO8hn1OsTy&{i^-}HM+K}#>1)O#f<^&02qXO)gv}ixjRva>T>xv@_N>0E$OZ$@c zFazBkN^D>-yc#}5#PTUtJ$n%>AAp|?R~By9dO_5!F>!VnG%Bh?9jY0 zS*M9=>(yMhGbDdJv%KY-rj1_1c-s6o>)|lBx$n<}*l@l!v)GbE>$gp9Mh?c@P<_Jq zruSqqeB_xk_<(-+y)AK=-)~YfI`HvQ_q)ruz0^|;$ORY@)&lPuu$8F@$h_GOWj_JT zq!;jx6C@mpepvF;OZtn6iXOw>7WdxE-b7*T<0BM$r@NhXK6drTKb>}dr(ee1N$f4S z0JL#1K8>cS*D>&+f%7vya%edCDH$&M35NzwZf-&X0)KxuxDO%4-fN=_b`qwA3WSHp zAxQK@0+W4_Kv=Uq$eQ^E@%ETS)l~IUpai>kiR^ZAmnQa(e6gF4o_w*0c_oez>9Sa2 zD3$o8Qtj|zol7!#=I=oNgU;YoJLdltp?E4B>+lmsr&=;$7=WWgnr8x|KdPa2f~^f zU{Ff}o`;PscNu|3!c%}l1CJZ+Je!fR0Gy#K_|)Hvzat;*=D4!QMffQ3R)YNj2@0BA z#YP0Hnbvb65CH<|b+}Te!T${XI>#&qGkmFxp`cAcK~Ao~ETA$k8*Ns%(v>o@Cse{O zn265nfO}C{S(%EQd%^w&)aV6#0E9Yc*RiwknV3tP^_=v`;S9IuQnIw-(jwr4g}@FE zHof@8Wi~D@dVWh)ZS7GordD4sfcOmf3!sv52u$%JCjmY_5+)F@PzJe^c9p zS$Gn=0TydmVV?|8*e{P0shQ8v(*if3nAgr{S^6jk;8_9{3Es`__2qk(0ze#+K)^l| zob=$kyk(G_{#ti^qWKw-TZBH>A?P0nM@V{Z%>q$QMN!cQF#GU%pD9`#$o2Y^ZeEtk4nSHh@}mEw`JYk-QPOE)$ahOEg;Ri zy1NSw;xyO_OrLUnP|Q?=+M6n?r&x+S^UAM+iE6ji|M}hCv*@8MuU^DoSBD*NN!;5~ zv#@KderjXj*1OCdR7(5(H`58zHeY`wdAXV!AWgNj$~~7xE*G8D*8gzp=k4Id$7~Zl z{~+T(A4wodU@z&JWHM<#G1ZzsKmHA}yH9F4r@&v+mC6q#8i&&g*!Q5+rTtDG5JL~& zP?i7`8$187xTM4?`?EnaC$W_QS&tsu8PA}q=ME-$ zCOnj|ErflKic3q;I$M5Fun3lvd6_k8CTL&wLB5N@KYnENiYV+w$j+-hHYFe@g-OYE zKojAHvXLi1@}&~Ar5p}30{d*JgGk|kr~^AAA-#Ys002lp)LTZ?*6sh& zgMtO1jzIV-%b*HY4%{@^AH~6j_Gvk)4(b8mza9AUC2Zm{n-Q*8szyde_t6ikya|}J zIZq2T6Juk`0jLb4v4s5o4TM*y+n(-^7|xVh5K5!z(U6&e6Y&KwqgU&xsi>%6V4#{0 zZsVsQA7Fr-?ZZD!6?MyP!_CxSL(@b+DFRGZ=_GtBp)MxH^@J`MpBk>apfHl_33dap>XGrCdbNb}rIGS&aO z`0Wk&>z^D2-rH4?d>2-Q@Cec7k2X} zyfv`1*Jo#Efu5>WbJbHqAX5!3Yyu|re-;E(jAwagA&}G;mn0|fJHfpl8A_ik}_K@hk!w%7ET$M5xBA11_xc> zoPu}(nBQaY)x^?(W3NIk>hEvmFCoJ)GGO$TUr+$Pp3V?v1zZ186tI?bn#Yp<3wHq= z^B`Wa0yQQO)*-)s{Vv!h13-#M6oTZeRR3x)w+6W-&^!P#;W32GAGn_F?CpJy8Lx7& z0KL8JMYJr5sAzR@u_F))fubn^6`K3^8{u06QhXEn^9TJ+fglfBgK$);PK>w<0MQ4m zw7Z%)d8rq$JJ@1Zpuz(Y!v0$tLCgC+7d`b03SubqK$(?sX&v0 zLd$_KWZ?*uWAmgTQF+WzvoYHtEfy)(KYx#8|4qdERr^fd&5ezow3ti`MuJi zv>`U(bHn%r^)lJmOxA_-(jxCK-k|(H!(3B0aAb%mjhmH5WJa=&M}rD+FZfS8e)9s^ zA(4Y-pY6W&$-faz$A0>M-^tkttYU$aa!>LOX?WgbP0KGgMguf-J*h{ z%r~?mUkw}lLD}I9DA>q1+p({ZTY&%{T3qQ?SO%zwz>{0k_u%r-yM*#`0qJamjLSpe za9}88T7UkIk^NRJhZ!eb_e{y<7tE<8lpEn#{i19QR|!yb1)r54rOJ7|%m3vRi`K$i zTpWhH4A#qAYJqD^5FArdQfjxSt+I+)L6;U8QWX-8C^D!^@GjY~9n!A;rTHgARPwvE zsOw+G51pWWO(YHYiWJq{Z`XadW~JIXuS@LxjXunq9EiSP?m%O zHE=&GtgzQK^FF>PDWwoOun9DM(Pr9+XEj-7X5h@nEs~nt%Scs-t)=xc@7~6SxnoSc z4ZU1Vrrv7tw?G}6n+PsT2QyEg+?Ma=$+V$2GVU=LpS|3cmp!ss0q4G5D>tC>(Hbcr zT?Tw@uOt8bZ{$&t+bTs@bf)W(U&>#hhu3f~#anApRi~;q1;?SLZ51lo>c<};`QxLjyrwf4sl(6^Q zoEc!41!~t3^Neo;W3a5XT%$IKFtpF3~{$^7P)mc*k^C?F3Z&XgTujFi2zWhG@M3fbf;M6yD5c6KQwTSiD` zp(05}R!dg9=e+v;@B2CKq)kBnRxnD=r-Dc>?5xYRqJHSNYPglP#!w$UkK*P z&F|)Kl+6f-E#GFmJHgBIu@RS#PR(cbui{k`dnF zyZT-~@aE*@%a>7Mf`w-?jMs2-cH*!C5)~1-<2;xRVB_=4d;3(LJbjAw{Kkzh^EpJQ zqJvjDS@i@=gnqrW6Q)o$+}wf;p0qLaIjtW3`zwslaOtPaXxPJXS8Rr7&zAN$pk^$| zMvMj1AAW&whx>tPtKhwG|Lm}UQqJHz4;C?7$X&2ymKm1yO0v*C`sZ8N(+-@0Dj|H#tp%cCl?9mZ zI8~?f^&s&fa)%=!J}ti6wUG^#M=a;2Jbc<2dIGL?qOLVdSJ8C>o&>$fH%k{av=VPe z%)6SNJv;H1n5v%%J9Fu$^!r>GD8i>|p1yS2HSkUQyU9dBoqbQY`P`kQl8@hxeVKag z`}Vk@-om?=GqD>NL_AL3Zwu+zUv2{0b?ddi$BDIarxW$)PJgNkqPi3re^F`j_QWnT zCVrmme&0@~YF-{5-_Mi!zXiuBpb5c%qt0U^{;6SpL=CRjd|!Gs23@j_Iz1ndOJPC5 z;&&a9kJLQ*l6tURVDGF7{{9tM739>y4?<8s@%uP#X77p+U0%6i+lh|radAXn$iNvG z&c|S1x5hTiB5Lyu@XT)@y#2H1>kdwA1Meg`CB;=L-rMna~(URru(4%4DgD{pbz|_y^M@3dX~QC7~oAtESG|O2uF} z$PGhg0Az1rVjgaYuf+UH)*;RzTtJ;(D_-CG$J*JluN>UFI5e?JA9VU=~##wJCKcq#-5E)?5p>go_t57~>tAM3!O zL#v-3{Vg&Fl#fiS5}USbjX{nQT*yjRTw&(8juAZKggFBX=|bn@)-A!`VQ;rc%ZwtY zz9`9Lk|kOiRdXPewMLDU$v5v$hP+ng;eP0@YhRhsNsP>mV!Za`9em}9iThSLkG?;0 zd|JTKv#MkugvREblgGujlQwjsru($4lkOI-T8Aar@@|jWZ6tq2>-&xFrta!DPWIIH zH)1NrE4DtRS~J;9VxJpHfjS;b4m*4V5Fur7*#mNlijFQw`CCn937|gq=wJEkZ?Qe2 zlZB#yz+5?3~3ZlfDoJ*Wj)ul$WNW$ z)xJ7XYO=QNPT)eBYaeLe)yn>;yh?DL$#@hs7N)6 zI*-#Ff)=l3irm`uY!Y7boZ`L95djIJdUq zqpX|fykJsas@-(k^n6)jc^{f1T&$u`+rrJqMS(b!HH?;7Ri2IGW9$Zl097T?(yfiI z+(I~LI4R`&U+TZ{Em4#iT?44x8tStg8O5~-q*uAH}MPZHog6bZ-l*^5|Dw`ed z)!+I7I~{V;!@0nUiCuSo+XEle#DMZx1_u5B@db{U$B)Ks$@QV8qq~I=H0X1)^I}Ys zVys3r(gA=_a7`grSX54L%Y0vR*MpHeeCE~Hxq@U zV(IR7?L=fPcVDnFKHy3fjXL`MaZgv*gVSM9Dnw-j8?l&t_{xrb^8V5=zWj#-B}(XlS45lyT<`*5vz1N1zC!tWIC)((wYO=Lb|m3g<*sqzNl>vpBcscYLM;-@^dD_)7bC+y|y*_qk78`+a$=!tub zHc0_J1x*^Gm0Pw-P^*di=y_A?C+MXwN+fB1vHd|K^-#v**ZbMorp?J93T09Zjxac7 zfPrk#rDhx%Ak5kt0nUQZf!H2G_Pce7`yQMjVt=>7@`Y6xWNiS0-%$xN%Y0aw&a3?u zK`A0pL{c{mg-C`ja7QcQCfeFq)3NT?)^3VF>O1gcs_&NN;nOM-Id8=iNCt%1;?{Ay z;MW4;3nwS+eZ9+q4!9^&lV{2ETQ-`X7q`m^PGo0!q4%gOmsy->|Ne}V=eF|vTb8z6 zA_tsPQV;d^S~#wiMU*}hmy(ZQ==Pvry;?+Grr>?M$aYu8)#N_b^GT;dn=Uq=_BN!HH4L+qmRt>Hp-~pgpElA~h z9jdrhBZzCC-#!AyNstX?;oHCeEXq+e7WP@8o~#dz2JGk%fhHzhAP(w z-X668^ubmEKR}u-_TD|Qb;Ag@o9m;>b>GxHCC;rnHL6Cs#b~%qwSIk8pc@+c;BPa# z3-e8#fh${Oh`;&ome_?~HS5bTTbL`XC3+W;`I`y!-R$V;nQ{=S*AZ6hn{wE7pO&CP zm-x=byYIcj%ZFk~4m8`}t>rK7j27_f5tJr797n3<;WmPxxjH|HAw=Anwf&e7KfmMe zAL|Hm(1n6z1)LYh`oIPIGJb=_U2D3kN=iVvSp1Cfg0v%pn-47p6XVVSwn28^#k{d%uFw^s{GJ*=3Nm);IJ0FJBRhtoSTQQ%;BKvY@1N_g?(E2OX~8`4xZYdUaMa)5X}(bb9GrrOHN*g zy=K;7Pp}=oz|5O=`}QlYS2U8dvs)Z{Qvl;UxbQXvzF?zHGFx-i4`ucPB^WvQ85_;0 zYmFKYKmuLdGFal^usXMCPqw66KzybcxF*ES{SFWat{;V72+J`#8U7s`o0}J!B>cH- z?u>Or{@P0Ac08*JkOGIoQhP^lN5=sh8#XsOGdtzN!oG|1ch1=J!wy`6?rL~RLB3AV z<_q!N>)+0AwNrL2%E*{Wh})5D!!Lb;Dsr}Nx6Q7=tJQ5|oeRY2(oMH}7sPnfq>G08 z@3E)OrX1=mHDl>H!_{>=U;Fc}=GB~4Rd_&sAr(8k=B`IZc4HZg*s*V5M*6L%i8W6m z-@TKKBs>;n5V#irez>?WlxMh1ZY2@Q1+1ZDXa(_!a54?|EP@Bv!dBXAUgK1@R_zAN ztwaQ0>PcT8A9Z_ISJ&IOkFb7Wupea1qYA(_oM!d!ViUsoh1cQrv}4qloqAoQ&~_EjA?(<2XvPyc9i5yK`QVQMLapvmiGGHUV&2qoD_$C3ddh5M&JsB&*x-Bj1CXHd6a3y@=LSrLH%80z51a^pSrMh9V}Y)d zn(l)vk zC!Vj%Y;1e;Y~cylDe3T6=c@gD8b=cx6Aqaj;)r1oOVt9UAIaxmz)O97deB*rGQ5>8 zb_NEqbgRt5;YDQUL5L3!=~7>9R}P~@86Oq3jg$davVIlN|G_>6>jGVT06aK374i z!Ap`+rGoM$rS?J0O3=;W?uDGvNN5+T&Nx7R8>+8lKqv-&V-rl+u?qXhzaf?3F-g9B z@lbkXq~u9vMO8fp>$X%(0O%4D6II?^TwV_hzX3n>RTEKB(UH)kq@BCeT8TX1Mpfki zi^ykzpYHZhJIQpUnkI_sN4~m53@UKh1P4%DITHM&@uH^x=j^FZCtGwnog{lUKCWje zEX(_j)LgiCLTGI-!vjlq8;UEAPF{&g0Xz16ou1jYgY(OJV1$+f1Ko(v(rtR0=e~>i zR~MzObYeX;uG@dM>4Au^IZ;?lIyp0=w}+pw5*%hM)|{HvGw}TTC4O*Xuxr7Q89*9p zA71rMNRtpme;BVRH?@jSskpm4ZU%+qN3g;}*t2uz&U%~k2*m*r?qp%1Or^e@YA=&n zT~!6XSBZkx5~8uV9tRb8>GBABdwY6fpyN6(5W-b!EC)MQg=jBKAIH+lcw z_*6Suu?X)%%;Hsl>kpH>f&vr_4fXZXli#H1soOg#lX3=I{D7inPfZ>PF=pD)y+==A z@!b`_h1?5oZ9mTxY;L^hbr4^AU;f-Bt*2*V*1^`8No;@A-)|Bp?iGvH!ocF{rdw{xe^45gw#~cw{<48 z(C%UEj8LNawrJ|>*TmN(iKor+&KvkCkTmw{m4(b|?jB+6lfuIJXWG%{r;OHgPKJbb zUB6DZ_|zRmQLrvqq8-Qj9!XKNvTswiLlSm#*LfB4quf_=GM@JLpo8|bm$tKWm`a|X zuS{Lt?juKle=n=^l>979?+Krqudc3^+rNKe#8i|h{g}#!U!AAAR<^=$fthAv&|rSP zcz$I{lAN~W?)VLD$=7>cn?=((7CmIknm}x100&U6O4ob8dGpbuM_DI( zD@-nNN}u0y#GLBV3rrKzU_mH@Nl>^-eT>~veD^;YB*y#GfdTVUQ$U1cW5hB1g9u@* zxdKx|PoUt>QwK?ibrclftbD+=ZD4?*E7#T0W3QNqh-s-QnmgdlBP#Dq;DV1xh7d;4_0c|(8xd|hLsa^6twIjW@7p+#s`r4yG}CUZNq!@*Wk`jR^RXhdD8?y*Zo z{dktRX^UHST=eoHsNnRn4UD#OKDCz%E|;qM|XGqzjqtZKtA{TWyd{q z5nqY8dj)@=(+(2rfEA$%i0!qjE27K;9g~uK95D5E(ws|6ts~ZidOSPR=Om(#nU&sS zCM;K9Z_s|bNzqrkC#lR;Dd$Ens%ZE{bjB%o!eL z$HL-b^w;lw zjXF<4aM)Ch)i^+(}4rk$;_u69gEDz+>H5nUb%%13I-^Kc5q)+Ig}K2=T7#B z#dWp!W%NRIHle+jHhMDpEga__jh}Fl(hEICo%d8HVl2kYRJ`BG)Urp4O(H8UZ}_wZ zZ$W-&p826sqN|how)M@PvbHZDHoImRWn=`>`yA};AJ#}4ZMk1ifI17F9b?fkij@2_ zrO*A(u1!SrG|e)ZZ&%TBV`$eWQFMEiT=sv<@o1#@vv(RhBy{V z92o96a1#&5nFK*rA{<(%VhQ;>+#4qHsXJz#g|{;nV_ZCJ()D7!148=DhP zbC);W(%xooYPTV!67==@vukIi=)$%PFSBoUUG3n;gB}!oPkrBS&{kbkJ^ANwgVK^z5a!wjgC7F^k&W$>&y+D#6*>C;ndt$F56XV zRle$TNEFxF;qqsJA=115w6It*4TGaVI=13+`q#^^RXtwO7;ak2XH`^HF_{HKoOHM3 zA`I@v&sICeZAGfv}mpEXKTHLIBx)EhX53)zGpl&>5VPANE1 z+F%$?$QdjyiY~DtehFovXjdmYww&iwR(;sgjJL!4y!?^oyy+gh1{$%6@)dy=Dma>x zxkxXui?;J_Av<&?4)K&5mYj-{@mTs=KH}9p7qoat0?;VZ|B3pPg*<}q)E}qDwq;_5HxgsTi;WI_g z3Z6ZQ^>QU-py-Rx^s4QaBn#3XkV)+%h?0}B*(xaiR35zr#Mye>Gfl1Nq;p|b*zpWiT>!1y#gzVI+lgh+*nn}PkbXT1xF^+ zuMU6r8S=Y4MIgO5ciR;YMl!>rY()Y|N!scWrA;FgAu8PEgd_9}%z^49@*}jHUta{h z&Tu=J1z|FdFJHNAD9A>xX3UFU$vL(yvUFl(?{S7GEk^mOW57FXxct28x2pz1_Al69_@B2FdNYB_3`m|LEU01?MV^4Chl80|2j@EWuO75uS z=^_jR<~>T&NASn2mDdR>z5D^>5TS#A0C~5!Rn|NG)^y3X0 z#%j5-g~f1|5cx;fs;CW`oi$HtiuTlaJN+9a$9lKodJby5Nsb&zD>Mv1zY!d4G`NXU zCKNn3ZRU6<3p9Ue4sI%bxbso5$U!gYQjoz!l6w1&J^$V+rC8TXky{UH&MsRW9BU?BZWtNqqhqx@jfv0NPLm(ayJ2>XkeKd(%OAQvboQIn(X1Tu6L!?q z|8**4C8Wv3Mkigp?TFXeCa#v7me;epB2DK`3!W5Q2>#Fgu%e)5-QpiW+4lvr;!4hq zR$Z+r!P{Sq>C&#KdNr+2wgkSF+jcQ{y=5}+f>K-Ue?A)iKP5=FGi#peXq??&jt$tn zOZY(0*V?MYyqAZh*dok(M;67gXPO_ic5@;?I>Qq>&k1Z5Z;{QVUo zN05lYS(}KS6+Cbv&<}*6E7>t!vUm9?w^n&w9NV_-1H!-%rN-RLSviAqLj)MG9yykT^Cj|xG9D}dlof59uNis&Ne({H@&%&)M`_oCw9`VeaJG*1GitTyA zg@tz?9vE8Q!^|9$$RrR~EzPD8AXj}S#ta5gA|npsTI}rAVPVhO+7N6QiOR|!-aY39 zlEJgd$!j$mN`Am6+j?>p_9@LCm9nGc<_2Q*wy>%f09An7 zo(RiuxqQ}ui_~*8n||RS3E^MaY3G0dxwsiqHJxoG6v{)5_s87O0R-JqoSg+zdr^N0 z`*$D*@%FyM7EK!6?o^O*5ihKpqFRD*eP*s`bl4Rva+|`$0#mO;l84jNCt~R0tbX4X zcuE`?FqIaru0CXGoq6{zLTiE2k-qtzK7x~%=_q5$dPEL7GP54i)wML6dDX7M!W`^X ze>;=?(|acmWFZrD$U-8iCZC_IlQj!GYSHxW2{Lb(o<``yS zVJR~e-rHSz5Lb%e0SC^tW@>54)!qHgS^y<3*Y+wLiTwPCD<8RS+-!uI56GqeU0@WG zNJzc;xcBKa6UCWIEkVJs%*?hoS=X!R;^NR(miLYAgLGc%J0-+x$w?gpeZfpWgp>E; z{=5X0bz?(g-@Z$c41xcC%CoX=EeN18w~E+c{QRuC=iuQ*iVZg}+G{(u_-Bu~4WJv% zKe=MOiHS*4QWCC{8i`sAL%9&@KVJ0{0|Rl-`e;L~ZYC$A@@Xs~&ar(S@Pc$YV$;b< z;v{I1NRMy;k{XPRdx|T{kfvrm*k{+usWkhx`9R%8O~O7gAyFsp@Vy+d9P$gn!D~uN zfmc;Ne{geM|^u^F$u@EKSL4)nL2o>r-0 zb4N+<$>1Q`glpl#w6*ijIN`a_GJ*6?AWT7ty}aD(RQ;bVdFm9G%>%NqEsd9~{nyJD z1~}R}B7?)W;L&ZPbPWrA(9$BmZ(nnA@|->Kc2VR^Om}>JRMfAaO$xuyUz2r2!>;V2 zxny_)Jg=vmN2K(9Em7a#5v#Nc7Lti>_7KCJFNtlls{@&b#A{I}#s9F5>F(|ZfD8Ju zsiT!OHNz96A zY`8p!YF=xjcj<=-OZHv2eLN*BA)y$Rvv%X}jUTC9+PvZ_u8F=QKDYmTU;Mo{hJijni_B_UC>Pi}6=0sCLIG?S zW)HAJ7$9aTn2b6?dA*mR`+0!dmNN6MuI_F@0Rf(HFhIYceIKcs2MZ4JE3MB1C=f|$ zDA&DOFgErVJZ_PtxWDJg6p}B_Bf_dGBi!5>02BT;A38?OecSxdJzHkm77RY| ztdyYNZ7AXIfhHqXlrxSQiy0CZt4;N*D<5;^W$%CK&4)XA%+|jg0use#(|vlu?AVbF^hCv@68>$3veG?NGmG zB2*Z|SedYo{7WcxD5)8Zo?aj7-`a_EG&D5y^c-Lbknp(|?d#t^3)R&>+#syidGa?0m2>O3k#FDr>*$~R za=v=)4*9YX$r+EV%Dn3`pBUV6F!jHwC4CXVuBw}T`V2+zL64;DlsQFk73@v&tJ+@_KeyYp|)H(o5} zh*Z_kNQv2FvNTJf`cg`|CIhAE7eSlj4i)?6h3~UX9$%4vTio1oB-#Owfy|1iez^|(?GJ=aAP(^h2>22|Pok2#2(0b_ zDuHBV4`c|UElhiz-Nw$;UTQoTeYHq$Kikn33ob5^tWud+uA!)%FW6fs_(ipM9O=6`@!fFQS3htWEBzGeWZaRjQhB?h>|)fTBW?N4@)7-K+WQT4a} z*RzRNQSsK6kBioEP~n5B`+IG17{mq(@Xz;*P;Ny`PQaJb;U<5q{rc!n;>PDwmB9fO z&CNZ}<;bO5J-fPMtK46IkoS3h=}$mhCj0rty<3LVJm_|O+HJGW%+e!i??lY{`{a4T zg`3T7g5O4cNlnn8Grb^_IgiWrn$@D-WnF^qxFqTgSQ;daDr&pmiNBWiN#WWJ#Zc_B zb`o&@WFw1{c&1sumGZ&E>T{P86a8~SmTso^ptbM;KDFVSkuqmAI8-$co=amRXQbxd z+u=zWN8n*Bbt)_M^GO?cBF+=u_r#{>&TU%^P^>beBM)9ZdQbaIvPNL-gVEJJhfYWe zm!HqIUx;~C{jiS6r%|?N$u<2>Mr?zb_^E~06;5T^BMa4?cGN!yZ3FfucJTU3w{#w( zORzM39mk{cn5&#hS23}hJG-W57gKv#hkzxc`I`12MiVtf-COGSDQ2tXUIb9Yt19Vg z+OgaxlrJp3Z`8fO)u$PCgisWi}}%v4tetaes+Sd-<(Ota?Wu@)b<4uLUK-Xp@FkZ7><H$uKn^P8n6 z*Lh*)v8PxXmHC$C>JNjHvn^v&&Qv13;OU-h^hNOlTz_^n#re+(s+V1;d*wUNr=>q9 zYB446V)*^UDM#zziJM1)h#Pe5G6=lIa9uJoDIpvk;tY}YX#WHMbIcZ$RaX4}p5z!& zd^Z2eaQpEb+b4f?Tb%4Z-08^};-nP77*q+r@k*qN&?WfIE%A+EH~G}IX9j!9M(G~$ z@M~IX)@hj?)S)+afAC_pV*a7u!&L`=2L&#qm8s@mp$@K&#VqViV>TpTMmEu^jXdiRS>KhOUp+ z`c}rqCD%H~>tnfCUvi7?zD}J-;*uyRWZEM0gIuNa3>?Ea;{}TS_Qvu3TkTT za;0YsjylyvBnTNfSOd!6|Jhor0_hGOJ6G^7-_-T%n%hn7NszzJ*xT>iv!`c%6Xqb*@So& z4+Qc)Ft{m{-QG0R*hZQ79YuOV6wtE;fq&g-7 zb2KFeuU3nXDeJf(OEfqS%7Xj}S_`8P$@Qm6Y~|jyXU|xviD0FLBINj?TqxWIZhq>Y zS()^op$|9I(4bU#;wJObp9kWiAoM3DsG#lJ`Z{r)IZ6%9%}}pE)PK6d?c;7bNg=JD zQ_2MsNn3{Y*1bD_A*E%%wC=fJS+q789ySq3lJ=}1-1JWoT5&-HLyCQqIIwITdp*LU zsc~@s>GAQ5KS7PD?K3MVh53w?i}lfXi^-T-nOCT@wNu&xxtyydb!J9}msyJ39C z9y*Ot4t)WWH-aksDuTM}dHEZzZj$MXEB0x1 z&)ozQU>ms#vH08_*HLi!|M#-F5#kbf$^prZ4Qp%O;Wru^gHQ}r(Yi;J5v%`yHFEAR zH#tLF41O5++>Dx6wAMBdVqz6H=GiD_^GMO z1RY)7@*krA#9%2}eAIbqX=x|q3^~|l)!1xQ$On7NjjUL=4(%;TU!rlko3tDCOI-LK zI+iqDN*zVo>+$iM8ygS3MR6v?2VU+HHgpUXvmV!y1o>kV{DXsoZEPm6QeZxLV($L^ z`RH$fm5|TEkTM_BUF5IWT45(w@NA;N=Ny>g)ZBC(L&Hude85W5#A5?N3*2XX%B``Du z4mv-;Af>Jajf@N94#dxMSN)cq(!2c0q_)Vk$=JWoY|lI;7b4y2 z+-lMJ>Y%SR=t?7vm^v`FnMCJz=2j_wY%48~3n24Q=1G83>wyFlsYK;s(acE% zGR{@D&8XCZk@2Hutnl|oe|s;yx(N<~b6=W~d;Y<6J>QKFmf=HK;Yx^_Bys?W!--zj zDkV)#!N4Ctj0IT{D`=6xdcG&A)AbS*op!C4k-JK!4~`F^*#75huNa^y1WpiLWXfF> zII_@=-KG}S1x7}UyLZ>wwUNU}G@KAO1N5ZXe+wv*7smoCsZ>*~Q_sKjN;v2^p@+WTv=FnIV1{|*_) zuH46}`qtA%IA5YHc&QLt01&v{wv!ZKH2@HYkrC_Lme`1BOzqvv@k(B{&GJDDLsgV| z8`$e$oCan1NSW=u!GVE4=*bD=8T%DaMys4zhsoI<&M>M};)Z2K;VSttUY+?OO@Gai zZ!G@;`#!$aOH)CfJ)JwqkESUF1SUBW>2#WkSmB_6j^hlV4`HWD82}h?Ii zdy*xC{VlyDKNrN`BJe~q9K3Kt>X4+QIb=}a0X@PZ50*l%a@f>BX&b-5*U2}=xq}~B zy5|^_-B@}M?EjD8_`6$Zzlz8e%gcl+o;!r7Z+tnbUTJ(LT109_6`tyAS9br)(nf97 z{=S|3-6i8W=}AWX6uj|9?)Zma)nVFN+{bAsR9+dgYBBPQv|5E`(6E`g+um#5QjKVRKot)ki8%sYjy81fyX zqUL5ism7CdjFvM?li$^lmCx_*YMhDZ6&9`MN0)aDmh9e>%pK16d_VW&+i5Pz0^z$& zw-$Sh4~WR=5?@c54&>@hNT!xL7%w$%re`ngjCJw9`m$U*y0er|w?{|m*u8~-WP`~h zZs!H9>t{pO%d$^=+xN*}7O#FQ zZ@&A}QFtNQwL0SXKHNb*btCwI1cT5EGjIfeZ)?l=Q0bFOeF{&No=UmB& ze^$Y~HK?kAkc`xb9RBxLj^*Nb_;91_<$X3gh1E5amB+P5pPMDuWv;xl*4;0+`>15tnJLpx39!`=Cf<4; z?;YZ5xg7b^GTD)NBVjo`fc)U$RE4BXm+Ci9ZvHa;R!V80s-G($w-VTljAb)N!S6g` zYXr9kg@n>p5>LBc%l8KZ7r7}q4b;93X~ful!@Py8^+QD6w`(fgKRy%14hKj&ziF7- z_eVTb`t3ehTd~!6;_Tn$Q*y-P&fo6VMqI!C{63G|u?C@=Rb|1+5AXK7R|`E{;2+fW zX=&KIe_q-7Bsh>+ev_V!Z{WNYIG224Xi>IN==$@Kku|Jbfr{A(l$$Fn8D(?H$y#T* zMUU%PXRh2M*7*5dw66U@{As;8-ANt0IKRFW$JwtCqA1I&I@TQI@RR8Kc2+Z;yiY!P zE?JWQB28_7?cGW1Q{0NCVkROi(A>O&YD=*9E0$_@c6PKj^G>Bn+7;b-JV%KH6Co@7 z34;)5DrG~?VRoAlSOs;Bp0J)u+rd$_vB8!dH1Kz+d}eM=r6D?_I3i+A@c8Vl!wJ1r zPcMXAEY54|AbKA4^y6=}S^qix_QLvwyYwRhMJ9p%X!3@I9x=)4&)?{#j`OgnJk#mD zYkShHZ=(ScBb1!%#Osh)BAG<_qp(qkj4aKBz*-!Uf1^kMisSUZZ~uDXdsV~cUq0I4 zE$^dK=S~%M%s!lO@lVr!*%QD4S5i-L66XV&zL6!tw?zp;E@-1$=e*?h#{Ky51H#R@ zS;a55=4ld@79W67kaim6Ar3N`po-&WtbYCa{3I(GS)cN?Xp;|1W;0Pc~u)$8Ms74)-z6g{Ti3Ej!aU$ z;2GSTF2F}U z#EXwR62C^(H)d8_6^>i|eR21XSvk*cHCcX2N;-UZk-$ZR9nLjfwQeh)Cl)(fjV_<% zzdns6_~`F7;&4g|i)BiASs7GH7A7Xu$br36WxB6+yn}BWDK#UhNv^6O=zU$~sC)7x z2c-~37~L_OTeh_q*p>cH1pjzYSex2EkdhrJ+~n&Hwb8MVmbGC)RVJg`ug7Cj#hj&N zGtW;<^*aUp{xS8w|IhleYSJkkMlw!YDK>ZF`SVH;ilV52_8g~Azk>@Apnzp>`Db&7 z%^WOS;MIQd;wm`ah)0FBpO=}HXTwDbIUly);zoHqbWH-QvzDLiX3k#++a{MEpA-u6 z4d>?C8+`Ik{gJdWV(fX+D%3c_7q#?ZmUKFz=@m8-*LhBwv)rvFAyI&@(GOQIv zwzw<48+|(AZjUyXSZ%4f5jS1>u~>|9JuHr{wA{-%z!YVJq#H*~4M`pv%Ju4_Z}D(F zTEC+ptL8~YrbYX>Go6KD_~5~!Xjc9H{-QSJt@`DTj?+t{2~VGDIXb>N|2Il{XkIbL z0M>zBpIeBB9@gx+cXCEOVW21B)_{kPkIS2e+|lc{mY2JYxCkx~#X*?^JnrL1vt1B` zo0*!zKNjMnWIT~x)FS<2<2kew!Da?28Ani1<3@w!-4|tL!syxQEicgz%FAtz=`-@N z@rO7#f~_+v?;f1_;iJS-peIoOz?5jyV&if6F~4s9+#GrDz>1PoZRX;KSp|iF)M%gj zx=CAeGwsu--PV_$h=nkg-l0pqd2RRVcJXL6o&;p{5E9^k_JEF_o>2wOUy*n-H)^O( z`n)uKesZ>>L|Lk<=e${eUgKd5w7bl)Tbz#W z{-sM1SGGKSltX_NEuN#J?G2RnoNSQ#F`LwWojJ5q(mIEiY{D5E2}t4)JizD$Kaq_k z@)6OU6@JR5rVm`6gx7B zeTwcnv5Rn!o$|a|_bn~avFu)ab3oF?dW(Z+);HPJ5(L82`m5e>){afnvOIQ^>2ZpF z{n4{rIy*Gu3^#=#XH_Jn`k!LNlT@JAoo{_LJ(-tp@);O!2+kX1wVLi=) zn_LS`wplOBiL2jt=NQbdO3cjY2}yaT-zw6!%{Db8)Q$q*bw`y-Uqe_eh zZ-@B!qLjYiyp++d8nxnQe@wL#cSWhSza!okx(;$EIIz1^^Uxb-g@5JeZ%}2~VqNEr zcO_?Lmh)R5oF4z^dyt3Z-`7VAu9LVvmYgGI{C=Ord3k@Vcy@hu#=lmKg`^Y{#C`^Ds&ZhkrJR9nH{l0J9ZHHsSEL#F7^$^Mt_U`_9{;( zF-Po3pGsWlG0g(+!+Mb_5AqH3wa)t0GO0MEhj(m#7AQBMu?3(dZCNNP!Et5UJ^R4Ue8}eDA zyu2O%+Kf&}(tvG9jmwwFKH+cMGjQCGgN$tWvZG~+BS~s<5_hNd*VVDtI87W}+a9a` z=atqLPsabeGCl^XBI&rii@*y1Wzx(4#`{Qbd%x{^e#qoXS=n)u|M2+8#Dntu{`a5z k-~HHA|NXE3|2WG(RCKiO_;oh?|M5h%)sL%HsMv-6Ki$(QegFUf diff --git a/doc/surya/surya_graph/surya_graph_DocumentEngineOwnable.sol.png b/doc/surya/surya_graph/surya_graph_DocumentEngineOwnable.sol.png index 882aa3f45b760bd883fe861eb3515c86dafe805b..23a2185246bcd42dde7e7b544fe19d6e3aef5c03 100644 GIT binary patch delta 69149 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 delta 69075 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