Skip to content

Commit 5d6494d

Browse files
authored
Merge pull request #33 from Synaps3Protocol/branch/audits
Branch/audits
2 parents 57da81b + 9a6d038 commit 5d6494d

108 files changed

Lines changed: 7605 additions & 3836 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/cov-badge.svg

Lines changed: 1 addition & 1 deletion
Loading

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ force-compile:
5454

5555
.PHONY: test ## run tests
5656
test:
57-
@export CI=true && forge test --show-progress --gas-report -vvvv
57+
@export CI=true && forge test --show-progress --gas-report -vvvv
58+
59+
.PHONY: testfork ## run tests
60+
testfork:
61+
@export CI=true && forge test --fork-url $(network) --gas-report -vvvv
5862

5963
.PHONY: coverage ## run tests coverage report
6064
coverage:

contracts/access/AccessManager.sol

Lines changed: 96 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -31,47 +31,116 @@ contract AccessManager is Initializable, UUPSUpgradeable, AccessManagerUpgradeab
3131
// return (true, getRoleAdmin(roleId), 0); // => (true, 0, 0)
3232
// }
3333

34-
// Strategic roles for governance classification within the protocol:
34+
// Multisig Assignment & Rotation
35+
// ───────────────────────────────────────────────────────────────
36+
//
37+
// All operational multisigs (Admin, Pauser, Councils, Treasury signers)
38+
// are granted and managed by the Community Governance (GOV_ROLE).
39+
// Any rotation, addition, or removal of a signer or council member
40+
// must be approved by governance through a proposal, queued in the Timelock,
41+
// and executed on-chain.
42+
//
43+
// This ensures that the execution layer (multisigs) remains accountable
44+
// to the collective will of the community.
45+
46+
// Strategic roles for governance classification within the protocol
47+
// ───────────────────────────────────────────────────────────────
3548
//
3649
// Community Governance Role:
3750
// - GOV_ROLE: Represents decentralized community governance.
38-
// Decisions are made through collective voting mechanisms (e.g., token-weighted, quadratic).
51+
// Decisions are made collectively through token-weighted, quadratic,
52+
// or other approved voting mechanisms, and executed via a Timelock
53+
// (e.g., 48–72 hours delay) for transparency and reaction time.
54+
//
55+
// Group / Council-Based Roles:
56+
// - ADMIN_ROLE: Managed by a multisig smart account.
57+
// Approves policy attestations, contract upgrades,
58+
// hook registrations, and moderates operational parameters.
59+
//
60+
// - SEC_ROLE: Managed by a designated security council multisig or EOA.
61+
// Authorized to pause protocol modules for monitoring, threat mitigation,
62+
// or emergency response; actions must be reported and are subject to limits.
63+
//
64+
// - TREASURER_ROLE: Managed by a treasury multisig smart account.
65+
// Executes disbursements and manages treasury flows within spending limits
66+
// and policies set by the Community Governance (GOV_ROLE).
67+
//
68+
// - CONTENT_COUNCIL_ROLE: Managed by a multisig smart account.
69+
// Participates in governance referenda and oversees content curation policies.
70+
//
71+
// - CUSTODY_COUNCIL_ROLE: Managed by a multisig smart account.
72+
// Participates in governance referenda for node/custodian validation policies.
73+
//
74+
// Individual / Contract-Based Roles:
75+
// - OPS_ROLE: Internal operational role assigned to protocol-trusted contracts,
76+
// enabling direct interaction with core modules. No human control.
77+
// - VER_ROLE: Individual role granted to trusted creators,
78+
// allowing them to upload content without conventional KYC-style verification.
79+
80+
// OPS_ROLE
81+
// ───────────────────────────────────────────────────────────────
82+
// Critical operational role used by internal protocol contracts
83+
// (e.g., Vault, Escrow) to call sensitive functions like lockFunds
84+
// and releaseFunds.
3985
//
40-
// Group/Council Based Roles:
41-
// - ADMIN_ROLE: Managed by a smart account or council.
42-
// Handles protocol upgrades, pause mechanisms, and operational role assignments.
43-
// - MOD_ROLE: Managed by a smart account or council.
44-
// Approves policy submissions and moderates hook operations.
45-
// - REF_ROLE: Managed by a smart account or council.
46-
// Participates in governance referenda for content curation and distributor selection.
86+
// The roleAdmin of OPS_ROLE is held by the ADMIN_ROLE multisig,
87+
// which itself is controlled by Community Governance (GOV_ROLE)
88+
// via proposals + timelock and supervised by SEC_ROLE guardian.
4789
//
48-
// Individual/Contract Based Roles:
49-
// - OPS_ROLE: Internal operational role assigned to protocol-trusted contracts
50-
// for direct module interactions. No human involvement.
51-
// - VER_ROLE: Individual role assigned to trusted creators, enabling
52-
// content uploads without conventional verification.
90+
// This design preserves flexibility to onboard future audited
91+
// protocol modules while preventing unilateral assignment:
92+
// any change requires a governance proposal, a timelock delay,
93+
// and transparent on-chain execution with published audit evidence.
94+
//
95+
// OPS_ROLE must never be granted to EOAs or multisigs directly.
5396

97+
// Hierarchy / Relationship Diagram
98+
// ───────────────────────────────────────────────────────────────
5499
/*
55-
GOV_ROLE (Community Governance)
100+
GOV_ROLE (Community Governance)
101+
102+
├── ADMIN_ROLE (Multisig Council)
103+
│ ├── OPS_ROLE (Internal Contract Role)
104+
│ └── SEC_ROLE (Security Council / Guardian)
105+
106+
├── TREASURER_ROLE (Treasury Multisig under GOV policy)
56107
57-
├── ADMIN_ROLE (Smart Account / Council)
58-
│ │
59-
│ ├── MOD_ROLE (Smart Account / Council)
60-
│ │
61-
│ └── OPS_ROLE (Internal Contract Role)
108+
├── CONTENT_COUNCIL_ROLE (Multisig Council)
62109
63-
├── REF_ROLE (Smart Account / Council)
110+
├── CUSTODY_COUNCIL_ROLE (Multisig Council)
64111
65-
── VER_ROLE (Individual Trusted Creator)
112+
── VER_ROLE (Individual Trusted Creator)
66113
*/
67114

68-
_setRoleAdmin(C.VER_ROLE, C.GOV_ROLE);
69-
_setRoleAdmin(C.REF_ROLE, C.GOV_ROLE);
70-
_setRoleAdmin(C.MOD_ROLE, C.ADMIN_ROLE);
115+
// Proposals Lifecycle (per-domain)
116+
// ───────────────────────────────────────────────────────────────
117+
// PROPOSER (domain governor/council) EXECUTOR (domain timelock)
118+
// └─────────────── propose/schedule ───────────────┘
119+
// domain ──> domain Timelock (delay) ──> execution on domain modules
120+
//
121+
// Example:
122+
// admin-governor (only allowlisted proposers) ──> AdminTimelock ──> Admin modules
123+
//
124+
// Notes:
125+
// • Each domain has its own Governor (proposers allowlisted) and its own Timelock.
126+
// • The domain Timelock is the ONLY authority recognized by that domain’s modules
127+
// (i.e., it holds the role or is the roleAdmin for that domain).
128+
// • EXECUTOR is typically open (EXECUTOR_ROLE = address(0)); anyone can execute after delay.
129+
// • SEC_ROLE: emergency actions MAY execute directly (no timelock) with strict scope limits.
130+
131+
// Role Admin Hierarchy (as configured)
132+
// ───────────────────────────────────────────────────────────────
133+
// Admin domain controls low-level ops & security roles:
71134
_setRoleAdmin(C.OPS_ROLE, C.ADMIN_ROLE);
72-
}
135+
_setRoleAdmin(C.SEC_ROLE, C.ADMIN_ROLE);
73136

74-
// TODO pause protocol based on permission and roles
137+
// Governance domain controls councils & treasury/community-facing roles:
138+
_setRoleAdmin(C.VER_ROLE, C.GOV_ROLE);
139+
_setRoleAdmin(C.ADMIN_ROLE, C.GOV_ROLE); // locked role
140+
_setRoleAdmin(C.TREASURER_ROLE, C.GOV_ROLE);
141+
_setRoleAdmin(C.CUSTODY_COUNCIL_ROLE, C.GOV_ROLE);
142+
_setRoleAdmin(C.CONTENT_COUNCIL_ROLE, C.GOV_ROLE);
143+
}
75144

76145
/// @dev Authorizes the upgrade of the contract.
77146
/// @notice Only the admin can authorize the upgrade.
Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,30 @@ import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC
1010
import { ERC721EnumerableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
1111
import { ERC721StatefulUpgradeable } from "@synaps3/core/primitives/upgradeable/ERC721StatefulUpgradeable.sol";
1212
import { AccessControlledUpgradeable } from "@synaps3/core/primitives/upgradeable/AccessControlledUpgradeable.sol";
13-
import { IAssetVerifiable } from "@synaps3/core/interfaces/assets/IAssetVerifiable.sol";
14-
import { IAssetOwnership } from "@synaps3/core/interfaces/assets/IAssetOwnership.sol";
13+
import { IAssetReferendumVerifiable } from "@synaps3/core/interfaces/assets/IAssetReferendumVerifiable.sol";
14+
import { IAssetRegistry } from "@synaps3/core/interfaces/assets/IAssetRegistry.sol";
1515

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

20-
/// @title AssetOwnership
20+
/// @title AssetRegistry
2121
/// @notice This contract manages ownership and lifecycle of digital assets using ERC721.
2222
/// @dev Implements UUPS upgradeability, access control, and stateful asset management.
23-
contract AssetOwnership is
23+
contract AssetRegistry is
2424
Initializable,
2525
UUPSUpgradeable,
2626
ERC721Upgradeable,
2727
AccessControlledUpgradeable,
2828
ERC721EnumerableUpgradeable,
2929
ERC721StatefulUpgradeable,
30-
IAssetOwnership
30+
IAssetRegistry
3131
{
3232
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
3333
/// @notice Reference to the asset verification contract for content approval.
3434
/// Our immutables behave as constants after deployment
3535
/// slither-disable-next-line naming-convention
36-
IAssetVerifiable public immutable ASSET_REFERENDUM;
36+
IAssetReferendumVerifiable public immutable ASSET_REFERENDUM;
3737

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

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

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

121120
// TODO: approved content get an incentive: a cooling mechanism is needed eg:
122121
// log decay, max registered asset rate, etc
@@ -125,7 +124,7 @@ contract AssetOwnership is
125124
/// @dev Requires approval before an asset can be registered.
126125
/// @param to The address that will own the minted NFT.
127126
/// @param assetId The unique identifier for the asset, serving as the NFT ID.
128-
function register(address to, uint256 assetId) external onlyApprovedAsset(to, assetId) {
127+
function register(address to, uint256 assetId) external whenNotPaused onlyApprovedAsset(to, assetId) {
129128
_mint(to, assetId);
130129
_enableAsset(assetId);
131130
emit RegisteredAsset(to, assetId);
@@ -135,15 +134,17 @@ contract AssetOwnership is
135134
/// @dev This action is irreversible and restricted to governance control.
136135
/// @param assetId The unique identifier of the asset to be revoked.
137136
function revoke(uint256 assetId) external restricted {
137+
address previousOwner = ownerOf(assetId);
138+
138139
_burn(assetId);
139140
_disableAsset(assetId);
140-
emit RevokedAsset(ownerOf(assetId), assetId);
141+
emit RevokedAsset(previousOwner, assetId);
141142
}
142143

143144
/// @notice Transfers an asset to a new owner.
144145
/// @param to The address of the new owner.
145146
/// @param assetId The unique identifier of the asset being transferred.
146-
function transfer(address to, uint256 assetId) external {
147+
function transfer(address to, uint256 assetId) external onlyOwner(assetId) {
147148
_transfer(msg.sender, to, assetId);
148149
emit TransferredAsset(msg.sender, to, assetId);
149150
}

contracts/assets/AssetSafe.sol

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils
55
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
66

77
import { AccessControlledUpgradeable } from "@synaps3/core/primitives/upgradeable/AccessControlledUpgradeable.sol";
8-
import { IAssetOwnership } from "@synaps3/core/interfaces/assets/IAssetOwnership.sol";
8+
import { IAssetRegistry } from "@synaps3/core/interfaces/assets/IAssetRegistry.sol";
99
import { IAssetSafe } from "@synaps3/core/interfaces/assets/IAssetSafe.sol";
1010
import { T } from "@synaps3/core/primitives/Types.sol";
1111

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

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

4747
/// @custom:oz-upgrades-unsafe-allow constructor
48-
constructor(address assetOwnership) {
48+
constructor(address assetRegistry) {
4949
_disableInitializers();
50-
ASSET_OWNERSHIP = IAssetOwnership(assetOwnership);
50+
ASSET_REGISTRY = IAssetRegistry(assetRegistry);
5151
}
5252

5353
/// @notice Initializes the proxy state.

contracts/core/interfaces/assets/IAssetReferendum.sol

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
// NatSpec format convention - https://docs.soliditylang.org/en/v0.5.10/natspec-format.html
33
pragma solidity 0.8.26;
44

5-
import { IAssetRegistrable } from "@synaps3/core/interfaces/assets/IAssetRegistrable.sol";
6-
import { IAssetVerifiable } from "@synaps3/core/interfaces/assets/IAssetVerifiable.sol";
7-
import { IAssetRevokable } from "@synaps3/core/interfaces/assets/IAssetRevokable.sol";
5+
import { IAssetReferendumRegistrable } from "@synaps3/core/interfaces/assets/IAssetReferendumRegistrable.sol";
6+
import { IAssetReferendumVerifiable } from "@synaps3/core/interfaces/assets/IAssetReferendumVerifiable.sol";
7+
import { IAssetReferendumRevokable } from "@synaps3/core/interfaces/assets/IAssetReferendumRevokable.sol";
88

99
/// @title IAssetReferendum
1010
/// @notice Unified interface for managing content registration and verifications within a referendum-based system.
11-
/// @dev This interface extends both IAssetRegistrable and IAssetVerifiable to provide a single entry point for
11+
/// @dev This interface extends both IAssetReferendumRegistrable and IAssetReferendumVerifiable to provide a single entry point for
1212
/// handling asset registration and verification processes.
13-
interface IAssetReferendum is IAssetRegistrable, IAssetVerifiable, IAssetRevokable {}
13+
interface IAssetReferendum is IAssetReferendumRegistrable, IAssetReferendumVerifiable, IAssetReferendumRevokable {}

contracts/core/interfaces/assets/IAssetRegistrable.sol renamed to contracts/core/interfaces/assets/IAssetReferendumRegistrable.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
// NatSpec format convention - https://docs.soliditylang.org/en/v0.5.10/natspec-format.html
33
pragma solidity 0.8.26;
44

5-
/// @title IAssetRegistrable Interface
5+
/// @title IAssetReferendumRegistrable Interface
66
/// @notice Defines the essential functions for managing asset registration and governance through a referendum process.
77
/// @dev This interface mirrors the FSM behavior from `IQuorum`, but scoped to asset governance.
8-
interface IAssetRegistrable {
8+
interface IAssetReferendumRegistrable {
99
/// @notice Submits a new asset proposition for a referendum.
1010
/// @dev This function should allow entities to propose an asset for approval.
1111
/// @param assetId The unique identifier of the asset being submitted.

contracts/core/interfaces/assets/IAssetRevokable.sol renamed to contracts/core/interfaces/assets/IAssetReferendumRevokable.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
// NatSpec format convention - https://docs.soliditylang.org/en/v0.5.10/natspec-format.html
33
pragma solidity 0.8.26;
44

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

0 commit comments

Comments
 (0)