Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ef60667
feat: added additional node role for technical referendum council
geolffreym Aug 20, 2025
9218fcc
test: fixed tests and added council logics
geolffreym Aug 20, 2025
00702f5
refactor: added pause protocol
geolffreym Aug 21, 2025
ba185ab
refactor: added pause protocol
geolffreym Aug 21, 2025
3f3ab1f
tests: invariant balance operator
geolffreym Aug 28, 2025
223d773
tests: invariant balance operator
geolffreym Aug 28, 2025
846bc01
tests: invariant balance operator
geolffreym Aug 28, 2025
a73c156
removed the uneeded exceed
geolffreym Sep 25, 2025
22497d3
Cache owner before burning revoked assets
geolffreym Sep 25, 2025
de26f6c
Merge pull request #31 from Synaps3Protocol/codex/conduct-a-security-…
geolffreym Sep 25, 2025
846a6e2
removed the uneeded exceed
geolffreym Sep 25, 2025
f75f0fa
removed the uneeded exceed
geolffreym Sep 25, 2025
169c70b
removed the uneeded exceed
geolffreym Sep 25, 2025
5af3107
Rename AssetOwnership to AssetRegistry
geolffreym Sep 25, 2025
9b913de
asset renames
geolffreym Sep 25, 2025
44a546b
asset renames
geolffreym Sep 25, 2025
0d8b712
Merge remote-tracking branch 'origin/codex/conduct-a-security-audit-u…
geolffreym Sep 25, 2025
8ef0990
test: coverage upgrade
geolffreym Sep 26, 2025
257de6f
test: coverage upgrade
geolffreym Sep 29, 2025
cb1c7e7
test: coverage upgrade
geolffreym Sep 29, 2025
016406f
test: coverage upgrade
geolffreym Sep 29, 2025
7d9d639
test: policy authorizer
geolffreym Oct 1, 2025
09809ce
test: right policy managet + authorizer
geolffreym Oct 13, 2025
ee01f14
test: right policy managet + authorizer
geolffreym Oct 13, 2025
ca69fc1
test: right policy managet + authorizer
geolffreym Oct 13, 2025
6a603d4
docs: pre-audits review doc
geolffreym Oct 13, 2025
0b4ab21
docs: pre-audits review doc
geolffreym Oct 13, 2025
4c2a6ec
Update SYNAPSE_AUDIT_DOC.md
geolffreym Oct 13, 2025
43aed7c
Update SYNAPSE_AUDIT_DOC.md
geolffreym Oct 13, 2025
dd3de78
Update SYNAPSE_AUDIT_DOC.md
geolffreym Oct 13, 2025
d723170
Update SYNAPSE_AUDIT_DOC.md
geolffreym Oct 13, 2025
e1189f6
docs: pre-audits review doc
geolffreym Oct 13, 2025
332c130
docs: pre-audits review doc
geolffreym Oct 14, 2025
560d237
docs: pre-audits review doc
geolffreym Oct 14, 2025
9a6d038
tests: fix tests
geolffreym Oct 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cov-badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ force-compile:

.PHONY: test ## run tests
test:
@export CI=true && forge test --show-progress --gas-report -vvvv
@export CI=true && forge test --show-progress --gas-report -vvvv

.PHONY: testfork ## run tests
testfork:
@export CI=true && forge test --fork-url $(network) --gas-report -vvvv

.PHONY: coverage ## run tests coverage report
coverage:
Expand Down
123 changes: 96 additions & 27 deletions contracts/access/AccessManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,47 +31,116 @@ contract AccessManager is Initializable, UUPSUpgradeable, AccessManagerUpgradeab
// return (true, getRoleAdmin(roleId), 0); // => (true, 0, 0)
// }

// Strategic roles for governance classification within the protocol:
// Multisig Assignment & Rotation
// ───────────────────────────────────────────────────────────────
//
// All operational multisigs (Admin, Pauser, Councils, Treasury signers)
// are granted and managed by the Community Governance (GOV_ROLE).
// Any rotation, addition, or removal of a signer or council member
// must be approved by governance through a proposal, queued in the Timelock,
// and executed on-chain.
//
// This ensures that the execution layer (multisigs) remains accountable
// to the collective will of the community.

// Strategic roles for governance classification within the protocol
// ───────────────────────────────────────────────────────────────
//
// Community Governance Role:
// - GOV_ROLE: Represents decentralized community governance.
// Decisions are made through collective voting mechanisms (e.g., token-weighted, quadratic).
// Decisions are made collectively through token-weighted, quadratic,
// or other approved voting mechanisms, and executed via a Timelock
// (e.g., 48–72 hours delay) for transparency and reaction time.
//
// Group / Council-Based Roles:
// - ADMIN_ROLE: Managed by a multisig smart account.
// Approves policy attestations, contract upgrades,
// hook registrations, and moderates operational parameters.
//
// - SEC_ROLE: Managed by a designated security council multisig or EOA.
// Authorized to pause protocol modules for monitoring, threat mitigation,
// or emergency response; actions must be reported and are subject to limits.
//
// - TREASURER_ROLE: Managed by a treasury multisig smart account.
// Executes disbursements and manages treasury flows within spending limits
// and policies set by the Community Governance (GOV_ROLE).
//
// - CONTENT_COUNCIL_ROLE: Managed by a multisig smart account.
// Participates in governance referenda and oversees content curation policies.
//
// - CUSTODY_COUNCIL_ROLE: Managed by a multisig smart account.
// Participates in governance referenda for node/custodian validation policies.
//
// Individual / Contract-Based Roles:
// - OPS_ROLE: Internal operational role assigned to protocol-trusted contracts,
// enabling direct interaction with core modules. No human control.
// - VER_ROLE: Individual role granted to trusted creators,
// allowing them to upload content without conventional KYC-style verification.

// OPS_ROLE
// ───────────────────────────────────────────────────────────────
// Critical operational role used by internal protocol contracts
// (e.g., Vault, Escrow) to call sensitive functions like lockFunds
// and releaseFunds.
//
// Group/Council Based Roles:
// - ADMIN_ROLE: Managed by a smart account or council.
// Handles protocol upgrades, pause mechanisms, and operational role assignments.
// - MOD_ROLE: Managed by a smart account or council.
// Approves policy submissions and moderates hook operations.
// - REF_ROLE: Managed by a smart account or council.
// Participates in governance referenda for content curation and distributor selection.
// The roleAdmin of OPS_ROLE is held by the ADMIN_ROLE multisig,
// which itself is controlled by Community Governance (GOV_ROLE)
// via proposals + timelock and supervised by SEC_ROLE guardian.
//
// Individual/Contract Based Roles:
// - OPS_ROLE: Internal operational role assigned to protocol-trusted contracts
// for direct module interactions. No human involvement.
// - VER_ROLE: Individual role assigned to trusted creators, enabling
// content uploads without conventional verification.
// This design preserves flexibility to onboard future audited
// protocol modules while preventing unilateral assignment:
// any change requires a governance proposal, a timelock delay,
// and transparent on-chain execution with published audit evidence.
//
// OPS_ROLE must never be granted to EOAs or multisigs directly.

// Hierarchy / Relationship Diagram
// ───────────────────────────────────────────────────────────────
/*
GOV_ROLE (Community Governance)
GOV_ROLE (Community Governance)
├── ADMIN_ROLE (Multisig Council)
│ ├── OPS_ROLE (Internal Contract Role)
│ └── SEC_ROLE (Security Council / Guardian)
├── TREASURER_ROLE (Treasury Multisig under GOV policy)
├── ADMIN_ROLE (Smart Account / Council)
│ │
│ ├── MOD_ROLE (Smart Account / Council)
│ │
│ └── OPS_ROLE (Internal Contract Role)
├── CONTENT_COUNCIL_ROLE (Multisig Council)
├── REF_ROLE (Smart Account / Council)
├── CUSTODY_COUNCIL_ROLE (Multisig Council)
── VER_ROLE (Individual Trusted Creator)
── VER_ROLE (Individual Trusted Creator)
*/

_setRoleAdmin(C.VER_ROLE, C.GOV_ROLE);
_setRoleAdmin(C.REF_ROLE, C.GOV_ROLE);
_setRoleAdmin(C.MOD_ROLE, C.ADMIN_ROLE);
// Proposals Lifecycle (per-domain)
// ───────────────────────────────────────────────────────────────
// PROPOSER (domain governor/council) EXECUTOR (domain timelock)
// └─────────────── propose/schedule ───────────────┘
// domain ──> domain Timelock (delay) ──> execution on domain modules
//
// Example:
// admin-governor (only allowlisted proposers) ──> AdminTimelock ──> Admin modules
//
// Notes:
// • Each domain has its own Governor (proposers allowlisted) and its own Timelock.
// • The domain Timelock is the ONLY authority recognized by that domain’s modules
// (i.e., it holds the role or is the roleAdmin for that domain).
// • EXECUTOR is typically open (EXECUTOR_ROLE = address(0)); anyone can execute after delay.
// • SEC_ROLE: emergency actions MAY execute directly (no timelock) with strict scope limits.

// Role Admin Hierarchy (as configured)
// ───────────────────────────────────────────────────────────────
// Admin domain controls low-level ops & security roles:
_setRoleAdmin(C.OPS_ROLE, C.ADMIN_ROLE);
}
_setRoleAdmin(C.SEC_ROLE, C.ADMIN_ROLE);

// TODO pause protocol based on permission and roles
// Governance domain controls councils & treasury/community-facing roles:
_setRoleAdmin(C.VER_ROLE, C.GOV_ROLE);
_setRoleAdmin(C.ADMIN_ROLE, C.GOV_ROLE); // locked role
_setRoleAdmin(C.TREASURER_ROLE, C.GOV_ROLE);
_setRoleAdmin(C.CUSTODY_COUNCIL_ROLE, C.GOV_ROLE);
_setRoleAdmin(C.CONTENT_COUNCIL_ROLE, C.GOV_ROLE);
}

/// @dev Authorizes the upgrade of the contract.
/// @notice Only the admin can authorize the upgrade.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,30 @@ import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC
import { ERC721EnumerableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import { ERC721StatefulUpgradeable } from "@synaps3/core/primitives/upgradeable/ERC721StatefulUpgradeable.sol";
import { AccessControlledUpgradeable } from "@synaps3/core/primitives/upgradeable/AccessControlledUpgradeable.sol";
import { IAssetVerifiable } from "@synaps3/core/interfaces/assets/IAssetVerifiable.sol";
import { IAssetOwnership } from "@synaps3/core/interfaces/assets/IAssetOwnership.sol";
import { IAssetReferendumVerifiable } from "@synaps3/core/interfaces/assets/IAssetReferendumVerifiable.sol";
import { IAssetRegistry } from "@synaps3/core/interfaces/assets/IAssetRegistry.sol";

// TODO: Evaluate ERC-404 for fractionalization support
// TODO: Evaluate ERC-2981 for royalty management
// TODO: Evaluate ERC-4804 for URL-based on-chain asset references

/// @title AssetOwnership
/// @title AssetRegistry
/// @notice This contract manages ownership and lifecycle of digital assets using ERC721.
/// @dev Implements UUPS upgradeability, access control, and stateful asset management.
contract AssetOwnership is
contract AssetRegistry is
Initializable,
UUPSUpgradeable,
ERC721Upgradeable,
AccessControlledUpgradeable,
ERC721EnumerableUpgradeable,
ERC721StatefulUpgradeable,
IAssetOwnership
IAssetRegistry
{
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
/// @notice Reference to the asset verification contract for content approval.
/// Our immutables behave as constants after deployment
/// slither-disable-next-line naming-convention
IAssetVerifiable public immutable ASSET_REFERENDUM;
IAssetReferendumVerifiable public immutable ASSET_REFERENDUM;

/// @dev Emitted when a new asset is registered on the platform.
/// @param owner The address of the creator or owner of the registered asset.
Expand Down Expand Up @@ -89,7 +89,7 @@ contract AssetOwnership is
/// https://forum.openzeppelin.com/t/what-does-disableinitializers-function-mean/28730/5
_disableInitializers();
// we need to verify that asset has passed the community approval.
ASSET_REFERENDUM = IAssetVerifiable(assetReferendum);
ASSET_REFERENDUM = IAssetReferendumVerifiable(assetReferendum);
}

/// @notice Initializes the upgradeable contract.
Expand All @@ -111,12 +111,11 @@ contract AssetOwnership is
return super.supportsInterface(interfaceId);
}

// TODO: build getURI => from custodian /erc721-metadata
// TODO: Update asset info control version restricted/approved by governance
// TODO: Transfer Ownership Fee: Introducing a fee for transferring
// ownership discourages frequent or unnecessary transfers,
// adding an economic cost to any potential abuse of the system. Like bypassing content
// verification by governance using a verified account.
// verification by governance using a verified account.

// TODO: approved content get an incentive: a cooling mechanism is needed eg:
// log decay, max registered asset rate, etc
Expand All @@ -125,7 +124,7 @@ contract AssetOwnership is
/// @dev Requires approval before an asset can be registered.
/// @param to The address that will own the minted NFT.
/// @param assetId The unique identifier for the asset, serving as the NFT ID.
function register(address to, uint256 assetId) external onlyApprovedAsset(to, assetId) {
function register(address to, uint256 assetId) external whenNotPaused onlyApprovedAsset(to, assetId) {
_mint(to, assetId);
_enableAsset(assetId);
emit RegisteredAsset(to, assetId);
Expand All @@ -135,15 +134,17 @@ contract AssetOwnership is
/// @dev This action is irreversible and restricted to governance control.
/// @param assetId The unique identifier of the asset to be revoked.
function revoke(uint256 assetId) external restricted {
address previousOwner = ownerOf(assetId);

_burn(assetId);
_disableAsset(assetId);
emit RevokedAsset(ownerOf(assetId), assetId);
emit RevokedAsset(previousOwner, assetId);
}

/// @notice Transfers an asset to a new owner.
/// @param to The address of the new owner.
/// @param assetId The unique identifier of the asset being transferred.
function transfer(address to, uint256 assetId) external {
function transfer(address to, uint256 assetId) external onlyOwner(assetId) {
_transfer(msg.sender, to, assetId);
emit TransferredAsset(msg.sender, to, assetId);
}
Expand Down
10 changes: 5 additions & 5 deletions contracts/assets/AssetSafe.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

import { AccessControlledUpgradeable } from "@synaps3/core/primitives/upgradeable/AccessControlledUpgradeable.sol";
import { IAssetOwnership } from "@synaps3/core/interfaces/assets/IAssetOwnership.sol";
import { IAssetRegistry } from "@synaps3/core/interfaces/assets/IAssetRegistry.sol";
import { IAssetSafe } from "@synaps3/core/interfaces/assets/IAssetSafe.sol";
import { T } from "@synaps3/core/primitives/Types.sol";

Expand All @@ -17,7 +17,7 @@ contract AssetSafe is Initializable, UUPSUpgradeable, AccessControlledUpgradeabl
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
/// Our immutables behave as constants after deployment
/// slither-disable-next-line naming-convention
IAssetOwnership public immutable ASSET_OWNERSHIP;
IAssetRegistry public immutable ASSET_REGISTRY;

/// @dev Mapping to securely store encrypted content using a unique key derived from assetId and cipher type.
mapping(bytes32 => bytes) private _secured;
Expand All @@ -38,16 +38,16 @@ contract AssetSafe is Initializable, UUPSUpgradeable, AccessControlledUpgradeabl
/// @param assetId The identifier of the asset.
/// @dev Reverts if the sender is not the owner of the asset based on the Ownership contract.
modifier onlyHolder(uint256 assetId) {
if (ASSET_OWNERSHIP.ownerOf(assetId) != msg.sender) {
if (ASSET_REGISTRY.ownerOf(assetId) != msg.sender) {
revert InvalidAssetRightsHolder();
}
_;
}

/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address assetOwnership) {
constructor(address assetRegistry) {
_disableInitializers();
ASSET_OWNERSHIP = IAssetOwnership(assetOwnership);
ASSET_REGISTRY = IAssetRegistry(assetRegistry);
}

/// @notice Initializes the proxy state.
Expand Down
10 changes: 5 additions & 5 deletions contracts/core/interfaces/assets/IAssetReferendum.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
// NatSpec format convention - https://docs.soliditylang.org/en/v0.5.10/natspec-format.html
pragma solidity 0.8.26;

import { IAssetRegistrable } from "@synaps3/core/interfaces/assets/IAssetRegistrable.sol";
import { IAssetVerifiable } from "@synaps3/core/interfaces/assets/IAssetVerifiable.sol";
import { IAssetRevokable } from "@synaps3/core/interfaces/assets/IAssetRevokable.sol";
import { IAssetReferendumRegistrable } from "@synaps3/core/interfaces/assets/IAssetReferendumRegistrable.sol";
import { IAssetReferendumVerifiable } from "@synaps3/core/interfaces/assets/IAssetReferendumVerifiable.sol";
import { IAssetReferendumRevokable } from "@synaps3/core/interfaces/assets/IAssetReferendumRevokable.sol";

/// @title IAssetReferendum
/// @notice Unified interface for managing content registration and verifications within a referendum-based system.
/// @dev This interface extends both IAssetRegistrable and IAssetVerifiable to provide a single entry point for
/// @dev This interface extends both IAssetReferendumRegistrable and IAssetReferendumVerifiable to provide a single entry point for
/// handling asset registration and verification processes.
interface IAssetReferendum is IAssetRegistrable, IAssetVerifiable, IAssetRevokable {}
interface IAssetReferendum is IAssetReferendumRegistrable, IAssetReferendumVerifiable, IAssetReferendumRevokable {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// NatSpec format convention - https://docs.soliditylang.org/en/v0.5.10/natspec-format.html
pragma solidity 0.8.26;

/// @title IAssetRegistrable Interface
/// @title IAssetReferendumRegistrable Interface
/// @notice Defines the essential functions for managing asset registration and governance through a referendum process.
/// @dev This interface mirrors the FSM behavior from `IQuorum`, but scoped to asset governance.
interface IAssetRegistrable {
interface IAssetReferendumRegistrable {
/// @notice Submits a new asset proposition for a referendum.
/// @dev This function should allow entities to propose an asset for approval.
/// @param assetId The unique identifier of the asset being submitted.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// NatSpec format convention - https://docs.soliditylang.org/en/v0.5.10/natspec-format.html
pragma solidity 0.8.26;

/// @title IAssetRevokable Interface
/// @title IAssetReferendumRevokable Interface
/// @notice Defines the functions for invalidating or withdrawing assets from the system.
/// @dev This interface focuses on asset rejection and revocation, used in the governance lifecycle.
interface IAssetRevokable {
interface IAssetReferendumRevokable {
/// @notice Rejects an asset proposition in the referendum.
/// @dev If rejected, the asset cannot be used in the system unless resubmitted.
/// @param assetId The unique identifier of the asset to be rejected.
Expand Down
Loading
Loading