This file is a merged representation of a subset of the codebase, containing files not matching ignore patterns, combined into a single document by Repomix. The content has been processed where content has been compressed (code blocks are separated by ⋮---- delimiter). # File Summary ## Purpose This file contains a packed representation of a subset of the repository's contents that is considered the most important context. It is designed to be easily consumable by AI systems for analysis, code review, or other automated processes. ## File Format The content is organized as follows: 1. This summary section 2. Repository information 3. Directory structure 4. Repository files (if enabled) 5. Multiple file entries, each consisting of: a. A header with the file path (## File: path/to/file) b. The full contents of the file in a code block ## Usage Guidelines - This file should be treated as read-only. Any changes should be made to the original repository files, not this packed version. - When processing this file, use the file path to distinguish between different files in the repository. - Be aware that this file may contain sensitive information. Handle it with the same level of security as you would the original repository. ## Notes - Some files may have been excluded based on .gitignore rules and Repomix's configuration - Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files - Files matching these patterns are excluded: docs/ - Files matching patterns in .gitignore are excluded - Files matching default ignore patterns are excluded - Content has been compressed - code blocks are separated by ⋮---- delimiter - Files are sorted by Git change count (files with more changes are at the bottom) # Directory Structure ``` .github/ ISSUE_TEMPLATE/ BUG_REPORT.yaml FEATURE_IMPROVEMENT.yaml workflows/ pre-commit.yaml test.yaml trufflehog.yml CODEOWNERS PULL_REQUEST_TEMPLATE.md audits/ Cyfrin-RelayContracts&Network.pdf Sherlock-RelayContracts&Network.pdf SigmaPrime-RelayContracts&Network.pdf script/ actions/ base/ ActionBase.sol ArbitraryCallBase.sol SetMaxNetworkLimitBase.sol SetMiddlewareBase.sol SetResolverBase.sol UpgradeProxyBase.sol interfaces/ ITimelockAction.sol ArbitraryCall.s.sol SetMaxNetworkLimit.s.sol SetMiddleware.s.sol SetResolver.s.sol UpdateBatch.s.sol UpgradeProxy.s.sol base/ DeployNetworkBase.sol DeployNetworkForVaultsBase.sol Logs.sol update-delay/ base/ ArbitraryCallUpdateDelayBase.sol DefaultUpdateDelayBase.sol SetMaxNetworkLimitUpdateDelayBase.sol SetMiddlewareUpdateDelayBase.sol SetResolverUpdateDelayBase.sol UpgradeProxyUpdateDelayBase.sol ArbitraryCallUpdateDelay.s.sol ColdActionsUpdateDelay.s.sol DefaultUpdateDelay.s.sol HotActionsUpdateDelay.s.sol SetMaxNetworkLimitUpdateDelay.s.sol SetMiddlewareUpdateDelay.s.sol SetResolverUpdateDelay.s.sol UpgradeProxyUpdateDelay.s.sol utils/ sort_errors.py sort_imports.py DeployNetwork.s.sol DeployNetworkForVaults.s.sol snapshots/ gas.txt sizes.txt src/ interfaces/ INetwork.sol ISetMaxNetworkLimitHook.sol Network.sol test/ Network.t.sol ui/ src/ abi.ts index.css main.tsx TimelockDashboard.tsx vite-env.d.ts wagmiConfig.tsx eslint.config.js index.html postcss.config.cjs tailwind.config.cjs tsconfig.app.json tsconfig.json tsconfig.node.json vite.config.mts .gitignore .gitmodules .nvmrc .pre-commit-config.yaml .prettierignore .prettierrc codecov.yml CONTRIBUTING.md foundry.lock foundry.toml LICENSE package.json README.md remappings.txt ``` # Files ## File: .github/ISSUE_TEMPLATE/BUG_REPORT.yaml ````yaml name: Bug report description: File a bug report to help us improve the code title: "[Bug]: " labels: ["bug"] body: - type: markdown attributes: value: | Please check that the bug is not already being tracked. - type: textarea attributes: label: Describe the bug description: Provide a clear and concise description of what the bug is and which contracts it affects. validations: required: true - type: textarea attributes: label: Expected Behavior description: Provide a clear and concise description of the desired fix. validations: required: true - type: textarea attributes: label: To Reproduce description: If you have written tests to showcase the bug, what can we run to reproduce the issue? placeholder: "git checkout / forge test --isolate --mt " - type: textarea attributes: label: Additional context description: If there is any additional context needed like a dependency or integrating contract that is affected please describe it below. ```` ## File: .github/ISSUE_TEMPLATE/FEATURE_IMPROVEMENT.yaml ````yaml name: Feature Improvement description: Suggest an improvement. labels: ["triage"] body: - type: markdown attributes: value: | Please ensure that the feature has not already been requested. - type: dropdown attributes: label: Component description: Which area of code does your idea improve? multiple: true options: - Gas Optimization - General design optimization (improving efficiency, cleanliness, or developer experience) - Testing - Documentation - type: textarea attributes: label: Describe the suggested feature and problem it solves. description: Provide a clear and concise description of what feature you would like to see, and what problems it solves. validations: required: true - type: textarea attributes: label: Describe the desired implementation. description: If possible, provide a suggested architecture change or implementation. - type: textarea attributes: label: Describe alternatives. description: If possible, describe the alternatives you've considered, or describe the current functionality and how it may be sub-optimal. - type: textarea attributes: label: Additional context. description: Please list any additional dependencies or integrating contacts that are affected. ```` ## File: .github/workflows/pre-commit.yaml ````yaml # checks that pre-commit hooks pass before allowing to merge a PR name: pre-commit on: pull_request: branches: [main, master, staging, dev, feat/**, fix/**] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: FOUNDRY_PROFILE: ${{ github.event_name == 'push' && 'ci' || 'pr' }} ETH_RPC_URL: ${{ secrets.ETH_RPC_URL }} jobs: pre-commit: runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: bullfrogsec/bullfrog@1831f79cce8ad602eef14d2163873f27081ebfb3 # v0.8.4 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: submodules: recursive - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "3.11" - name: Install Foundry uses: foundry-rs/foundry-toolchain@82dee4ba654bd2146511f85f0d013af94670c4de # v1.4.0 with: version: stable - name: Install pre-commit run: python -m pip install --upgrade pip pre-commit - name: Run pre-commit run: pre-commit run --all-files --color always --show-diff-on-failure env: SKIP: forge-snapshots,doc ```` ## File: .github/workflows/test.yaml ````yaml name: test on: pull_request: branches: [main, master, staging, dev, feat/**, fix/**] push: branches: [main, master, staging, dev] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: FOUNDRY_PROFILE: ${{ github.event_name == 'push' && 'ci' || 'pr' }} ETH_RPC_URL: ${{ secrets.ETH_RPC_URL }} jobs: forge-test: name: Foundry project runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: bullfrogsec/bullfrog@1831f79cce8ad602eef14d2163873f27081ebfb3 # v0.8.4 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: submodules: recursive - name: Install Foundry uses: foundry-rs/foundry-toolchain@82dee4ba654bd2146511f85f0d013af94670c4de # v1.4.0 with: version: stable - name: Run Forge fmt check run: forge fmt --check - name: Run Forge tests run: forge test --isolate - name: Run Forge coverage run: | forge coverage --report lcov id: coverage env: COVERAGE: true - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} ```` ## File: .github/workflows/trufflehog.yml ````yaml name: TruffleHog on: pull_request: types: [opened, synchronize, reopened] permissions: contents: read pull-requests: write id-token: write issues: write concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: scan: runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: bullfrogsec/bullfrog@1831f79cce8ad602eef14d2163873f27081ebfb3 # v0.8.4 - name: Checkout code uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 - name: TruffleHog OSS id: trufflehog uses: trufflesecurity/trufflehog@0f58ae7c5036094a1e3e750d18772af92821b503 # v3.90.5 with: path: . base: ${{ github.event.pull_request.base.sha }} head: ${{ github.event.pull_request.head.sha }} extra_args: --results=verified,unknown ```` ## File: .github/CODEOWNERS ```` * @symbioticfi/contracts ```` ## File: .github/PULL_REQUEST_TEMPLATE.md ````markdown # Pull Request ## Description Please include a summary of the change and which feature was implemented or which issue was fixed. Also, include relevant motivation and context. List any dependencies that are required for this change. Fixes # (issue) ### How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. # Checklist: - [ ] 100% test and branch coverage - [ ] check slither or other analyzer for severe issues - [ ] fuzz and invariant tests (when applicable) --- ### Considerations - I have followed the [contributing guidelines](../CONTRIBUTING.md). - My code follows the style guidelines of this project and I have run `forge fmt` and prettier to ensure the code style is valid - I have performed a self-review of my own code - I have commented my code, particularly in hard-to-understand areas - I have made corresponding changes to the documentation - I have added tests that prove my fix is effective or that my feature works - New and existing unit tests pass locally with my changes ### Additional context Add any other context about the pull request here. ```` ## File: script/actions/base/ActionBase.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {Script} from "forge-std/Script.sol"; ⋮---- import "../../base/Logs.sol"; import {ITimelockAction} from "../interfaces/ITimelockAction.sol"; ⋮---- import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; ⋮---- contract ActionBase is Script, Logs { ⋮---- function callTimelock(TimelockParams memory params) internal { ⋮---- function callTimelockBatch(TimelockBatchParams memory params) internal { ⋮---- // Create values array filled with zeros ```` ## File: script/actions/base/ArbitraryCallBase.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./ActionBase.sol"; ⋮---- contract ArbitraryCallBase is ActionBase { ⋮---- function runSchedule() public { ⋮---- function runExecute() public { ⋮---- function runScheduleAndExecute() public { ```` ## File: script/actions/base/SetMaxNetworkLimitBase.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./ActionBase.sol"; import {Network} from "../../../src/Network.sol"; ⋮---- import {IBaseDelegator} from "@symbioticfi/core/src/interfaces/delegator/IBaseDelegator.sol"; import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol"; import {Subnetwork} from "@symbioticfi/core/src/contracts/libraries/Subnetwork.sol"; import {ITimelockAction} from "../interfaces/ITimelockAction.sol"; ⋮---- contract SetMaxNetworkLimitBase is ActionBase, ITimelockAction { ⋮---- function runSchedule() public { ⋮---- function runExecute() public { ⋮---- function runScheduleAndExecute() public { ⋮---- function getTargetAndPayload() public view returns (address target, bytes memory payload) { ```` ## File: script/actions/base/SetMiddlewareBase.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./ActionBase.sol"; import {Network} from "../../../src/Network.sol"; ⋮---- import {INetworkMiddlewareService} from "@symbioticfi/core/src/interfaces/service/INetworkMiddlewareService.sol"; import {SymbioticCoreConstants} from "@symbioticfi/core/test/integration/SymbioticCoreConstants.sol"; import {ITimelockAction} from "../interfaces/ITimelockAction.sol"; ⋮---- contract SetMiddlewareBase is ActionBase, ITimelockAction { ⋮---- function runSchedule() public { ⋮---- function runExecute() public { ⋮---- function runScheduleAndExecute() public { ⋮---- function getTargetAndPayload() public view returns (address target, bytes memory payload) { ```` ## File: script/actions/base/SetResolverBase.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./ActionBase.sol"; ⋮---- import {IVetoSlasher} from "@symbioticfi/core/src/interfaces/slasher/IVetoSlasher.sol"; import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol"; import {Subnetwork} from "@symbioticfi/core/src/contracts/libraries/Subnetwork.sol"; import {ITimelockAction} from "../interfaces/ITimelockAction.sol"; ⋮---- contract SetResolverBase is ActionBase, ITimelockAction { ⋮---- function runSchedule() public { ⋮---- function runExecute() public { ⋮---- function runScheduleAndExecute() public { ⋮---- function getTargetAndPayload() public view returns (address target, bytes memory payload) { ```` ## File: script/actions/base/UpgradeProxyBase.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./ActionBase.sol"; import {Network} from "../../../src/Network.sol"; ⋮---- import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; import {ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {ITimelockAction} from "../interfaces/ITimelockAction.sol"; ⋮---- contract UpgradeProxyBase is ActionBase, ITimelockAction { ⋮---- function runSchedule() public { ⋮---- function runExecute() public { ⋮---- function runScheduleAndExecute() public { ⋮---- function _getProxyAdmin(address proxy) internal view returns (address admin) { ⋮---- function getTargetAndPayload() public view returns (address target, bytes memory payload) { ```` ## File: script/actions/interfaces/ITimelockAction.sol ```` // SPDX-License-Identifier: MIT ⋮---- interface ITimelockAction { /** * @notice Schedule the action through the timelock */ function runSchedule() external; ⋮---- /** * @notice Execute the action immediately through the timelock */ function runExecute() external; ⋮---- /** * @notice Schedule and execute the action through the timelock */ function runScheduleAndExecute() external; ⋮---- /** * @notice Get the target and payload of the action */ function getTargetAndPayload() external view returns (address, bytes memory); ```` ## File: script/actions/ArbitraryCall.s.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./base/ArbitraryCallBase.sol"; ⋮---- contract ArbitraryCall is ArbitraryCallBase { // Configuration constants - UPDATE THESE BEFORE EXECUTING ⋮---- // Address of the Network ⋮---- // Address of the Target ⋮---- // Data to pass to the Target ⋮---- // Delay for the action to be executed ⋮---- // Optional ⋮---- // Salt for TimelockController operations ⋮---- /** * @notice Schedule an arbitrary function call through the timelock */ function runS() public { ⋮---- /** * @notice Execute an arbitrary function call immediately through the timelock */ function runE() public { ⋮---- /** * @notice Schedule and execute an arbitrary function call through the timelock * @dev It will succeed only if the delay is 0 */ function runSE() public { ```` ## File: script/actions/SetMaxNetworkLimit.s.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./base/SetMaxNetworkLimitBase.sol"; ⋮---- contract SetMaxNetworkLimit is SetMaxNetworkLimitBase { // Configuration constants - UPDATE THESE BEFORE EXECUTING ⋮---- // Address of the Network ⋮---- // Address of the Vault ⋮---- // Maximum amount of delegation that network is ready to receive ⋮---- // Delay for the action to be executed ⋮---- // Optional ⋮---- // Subnetwork Identifier (multiple subnetworks can be used, e.g., to have different max network limits for the same network) ⋮---- // Salt for TimelockController operations ⋮---- /** * @notice Schedule a setMaxNetworkLimit through the timelock */ function runS() public { ⋮---- /** * @notice Execute a setMaxNetworkLimit immediately through the timelock */ function runE() public { ⋮---- /** * @notice Schedule and execute a setMaxNetworkLimit through the timelock * @dev It will succeed only if the delay is 0 */ function runSE() public { ```` ## File: script/actions/SetMiddleware.s.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./base/SetMiddlewareBase.sol"; ⋮---- contract SetMiddleware is SetMiddlewareBase { // Configuration constants - UPDATE THESE BEFORE EXECUTING ⋮---- // Address of the Network ⋮---- // Address of the Middleware ⋮---- // Delay for the action to be executed ⋮---- // Optional ⋮---- // Salt for TimelockController operations ⋮---- /** * @notice Schedule a setMiddleware through the timelock */ function runS() public { ⋮---- /** * @notice Execute a setMiddleware immediately through the timelock */ function runE() public { ⋮---- /** * @notice Schedule and execute a setMiddleware through the timelock * @dev It will succeed only if the delay is 0 */ function runSE() public { ```` ## File: script/actions/SetResolver.s.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./base/SetResolverBase.sol"; ⋮---- contract SetResolver is SetResolverBase { // Configuration constants - UPDATE THESE BEFORE EXECUTING ⋮---- // Address of the Network ⋮---- // Address of the Vault ⋮---- // Address of the Resolver ⋮---- // Delay for the action to be executed ⋮---- // Optional ⋮---- // Subnetwork Identifier (multiple subnetworks can be used, e.g., to have different resolvers for the same network) ⋮---- // Hints for the Resolver ⋮---- // Salt for TimelockController operations ⋮---- /** * @notice Schedule a setResolver through the timelock */ function runS() public { ⋮---- /** * @notice Execute a setResolver immediately through the timelock */ function runE() public { ⋮---- /** * @notice Schedule and execute a setResolver through the timelock * @dev It will succeed only if the delay is 0 */ function runSE() public { ```` ## File: script/actions/UpdateBatch.s.sol ```` // // SPDX-License-Identifier: MIT // pragma solidity ^0.8.25; ⋮---- // import {ITimelockAction} from "./interfaces/ITimelockAction.sol"; // import {ActionBase} from "./base/ActionBase.sol"; // import {SetMaxNetworkLimitBase} from "./base/SetMaxNetworkLimitBase.sol"; // import {SetMiddlewareBase} from "./base/SetMiddlewareBase.sol"; // import {SetResolverBase} from "./base/SetResolverBase.sol"; // import {UpgradeProxyBase} from "./base/UpgradeProxyBase.sol"; ⋮---- // contract UpdateBatch is ActionBase { // // Configuration constants - UPDATE THESE BEFORE EXECUTING ⋮---- // // Address of the Network // address constant NETWORK = 0x0000000000000000000000000000000000000000; // // Delay for the action to be executed // uint256 constant DELAY = 14 days; // // Salt for TimelockController operations // bytes32 constant SALT = "UpdateBatch"; ⋮---- // // Address of the Vault // address constant VAULT1 = 0x0000000000000000000000000000000000000000; // // Subnetwork Identifier (multiple subnetworks can be used, e.g., to have different resolvers for the same network) // uint96 constant SUBNETWORK_IDENTIFIER1 = 0; // // Maximum amount of delegation that network is ready to receive // uint256 constant MAX_NETWORK_LIMIT1 = 0; ⋮---- // address constant VAULT2 = 0x0000000000000000000000000000000000000000; ⋮---- // uint96 constant SUBNETWORK_IDENTIFIER2 = 0; ⋮---- // uint256 constant MAX_NETWORK_LIMIT2 = 0; ⋮---- // // Address of the Middleware // address constant MIDDLEWARE = 0x0000000000000000000000000000000000000000; // // Address of the Resolver // address constant RESOLVER = 0x0000000000000000000000000000000000000000; // // Address of the New Implementation // address constant NEW_IMPLEMENTATION = 0x0000000000000000000000000000000000000000; // // Data to pass to the new implementation after upgrade // bytes constant UPGRADE_DATA = hex""; ⋮---- // // Hints for the Resolver // bytes constant HINTS = hex""; ⋮---- // ITimelockAction[] public actions; ⋮---- // constructor() { // // Add all actions needed for the update batch to the array // actions.push( // new SetMaxNetworkLimitBase( // SetMaxNetworkLimitBase.SetMaxNetworkLimitParams({ // network: NETWORK, // vault: VAULT1, // subnetworkId: SUBNETWORK_IDENTIFIER1, // maxNetworkLimit: MAX_NETWORK_LIMIT1, // delay: DELAY, // salt: SALT // }) // ) // ); ⋮---- // vault: VAULT2, // subnetworkId: SUBNETWORK_IDENTIFIER2, // maxNetworkLimit: MAX_NETWORK_LIMIT2, ⋮---- // new SetMiddlewareBase( // SetMiddlewareBase.SetMiddlewareParams({ ⋮---- // middleware: MIDDLEWARE, ⋮---- // new SetResolverBase( // SetResolverBase.SetResolverParams({ ⋮---- // identifier: SUBNETWORK_IDENTIFIER1, // resolver: RESOLVER, // hints: HINTS, ⋮---- // new UpgradeProxyBase( // UpgradeProxyBase.UpgradeProxyParams({ ⋮---- // newImplementation: NEW_IMPLEMENTATION, // upgradeData: UPGRADE_DATA, ⋮---- // } ⋮---- // function runS() public { // callTimelockBatch( // TimelockBatchParams({network: NETWORK, isExecutionMode: false, actions: actions, delay: DELAY, salt: SALT}) ⋮---- // function runE() public { ⋮---- // TimelockBatchParams({network: NETWORK, isExecutionMode: true, actions: actions, delay: 0, salt: SALT}) ⋮---- // function runSE() public { // runS(); // runE(); ⋮---- // } ```` ## File: script/actions/UpgradeProxy.s.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./base/UpgradeProxyBase.sol"; ⋮---- contract UpgradeProxy is UpgradeProxyBase { // Configuration constants - UPDATE THESE BEFORE EXECUTING ⋮---- // Address of the Network ⋮---- // Address of the new implementation ⋮---- // Data to pass to the new implementation after upgrade ⋮---- // Delay for the action to be executed ⋮---- // Optional ⋮---- // Salt for TimelockController operations ⋮---- /** * @notice Schedule a network upgrade through the timelock */ function runS() public { ⋮---- /** * @notice Execute a network upgrade immediately through the timelock */ function runE() public { ⋮---- /** * @notice Schedule and execute a network upgrade through the timelock * @dev It will succeed only if the delay is 0 */ function runSE() public { ```` ## File: script/base/DeployNetworkBase.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {Script} from "forge-std/Script.sol"; ⋮---- import "./Logs.sol"; import {Network} from "../../src/Network.sol"; import {INetwork} from "../../src/interfaces/INetwork.sol"; ⋮---- import {SymbioticCoreConstants} from "@symbioticfi/core/test/integration/SymbioticCoreConstants.sol"; import {INetworkMiddlewareService} from "@symbioticfi/core/src/interfaces/service/INetworkMiddlewareService.sol"; import {IBaseDelegator} from "@symbioticfi/core/src/interfaces/delegator/IBaseDelegator.sol"; import {IVetoSlasher} from "@symbioticfi/core/src/interfaces/slasher/IVetoSlasher.sol"; import {CreateXWrapper} from "@symbioticfi/core/script/utils/CreateXWrapper.sol"; ⋮---- import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; ⋮---- contract DeployNetworkBase is Script, Logs, CreateXWrapper { ⋮---- bytes11 salt; // Salt for CREATE3 deterministic deployment ⋮---- function runBase(DeployNetworkParams memory params) public returns (address) { ⋮---- // Needed for permissioned deploy protection ⋮---- // CreateX-specific salt generation ⋮---- // Create initialization code for TransparentUpgradeableProxy ⋮---- // Deploy proxy using CREATE3 ⋮---- // if target is address(0), it means that the delay is for the any address ⋮---- function _getProxyAdmin(address proxy) internal view returns (address admin) { ```` ## File: script/base/DeployNetworkForVaultsBase.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {DeployNetworkBase} from "./DeployNetworkBase.sol"; import {Network} from "../../src/Network.sol"; import {INetwork} from "../../src/interfaces/INetwork.sol"; ⋮---- import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol"; import {IBaseDelegator} from "@symbioticfi/core/src/interfaces/delegator/IBaseDelegator.sol"; import {IVetoSlasher} from "@symbioticfi/core/src/interfaces/slasher/IVetoSlasher.sol"; import {Subnetwork} from "@symbioticfi/core/src/contracts/libraries/Subnetwork.sol"; ⋮---- import {AccessControl} from "openzeppelin-contracts/contracts/access/AccessControl.sol"; import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; ⋮---- contract DeployNetworkForVaultsBase is DeployNetworkBase { ⋮---- function runBase(DeployNetworkForVaultsParams memory params) public returns (address) { ⋮---- // update deploy network params to include deployer as proposer and executor ⋮---- // deploy network ⋮---- // update network for vaults ⋮---- function updateDeployParamsForDeployer(DeployNetworkParams memory deployNetworkParams) ⋮---- // clone the struct ⋮---- function updateNetworkForVaults( ```` ## File: script/base/Logs.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {Script, console2} from "forge-std/Script.sol"; import {Vm, VmSafe} from "forge-std/Vm.sol"; ⋮---- contract Logs is Script { ⋮---- function log(string memory data) internal { ```` ## File: script/update-delay/base/ArbitraryCallUpdateDelayBase.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "../../actions/base/ActionBase.sol"; import {Network} from "../../../src/Network.sol"; import {INetwork} from "../../../src/interfaces/INetwork.sol"; ⋮---- import {ITimelockAction} from "../../actions/interfaces/ITimelockAction.sol"; ⋮---- contract ArbitraryCallUpdateDelayBase is ActionBase, ITimelockAction { ⋮---- function runSchedule() public { ⋮---- function runExecute() public { ⋮---- function runScheduleAndExecute() public { ⋮---- function getTargetAndPayload() public view returns (address target_, bytes memory payload) { ```` ## File: script/update-delay/base/DefaultUpdateDelayBase.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "../../actions/base/ActionBase.sol"; import {Network} from "../../../src/Network.sol"; ⋮---- import { TimelockControllerUpgradeable } from "@openzeppelin/contracts-upgradeable/governance/TimelockControllerUpgradeable.sol"; import {ITimelockAction} from "../../actions/interfaces/ITimelockAction.sol"; ⋮---- contract DefaultUpdateDelayBase is ActionBase, ITimelockAction { ⋮---- function runSchedule() public { ⋮---- function runExecute() public { ⋮---- function runScheduleAndExecute() public { ⋮---- function getTargetAndPayload() public view returns (address target, bytes memory payload) { ```` ## File: script/update-delay/base/SetMaxNetworkLimitUpdateDelayBase.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "../../actions/base/ActionBase.sol"; import {Network} from "../../../src/Network.sol"; import {INetwork} from "../../../src/interfaces/INetwork.sol"; ⋮---- import {IBaseDelegator} from "@symbioticfi/core/src/interfaces/delegator/IBaseDelegator.sol"; import {ITimelockAction} from "../../actions/interfaces/ITimelockAction.sol"; ⋮---- contract SetMaxNetworkLimitUpdateDelayBase is ActionBase, ITimelockAction { ⋮---- function runSchedule() public { ⋮---- function runExecute() public { ⋮---- function runScheduleAndExecute() public { ⋮---- function getTargetAndPayload() public view returns (address target, bytes memory payload) { ```` ## File: script/update-delay/base/SetMiddlewareUpdateDelayBase.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "../../actions/base/ActionBase.sol"; import {Network} from "../../../src/Network.sol"; import {INetwork} from "../../../src/interfaces/INetwork.sol"; import {ITimelockAction} from "../../actions/interfaces/ITimelockAction.sol"; ⋮---- import {SymbioticCoreConstants} from "@symbioticfi/core/test/integration/SymbioticCoreConstants.sol"; import {INetworkMiddlewareService} from "@symbioticfi/core/src/interfaces/service/INetworkMiddlewareService.sol"; ⋮---- contract SetMiddlewareUpdateDelayBase is ActionBase, ITimelockAction { ⋮---- function runSchedule() public { ⋮---- function runExecute() public { ⋮---- function runScheduleAndExecute() public { ⋮---- function getTargetAndPayload() public view returns (address target, bytes memory payload) { ```` ## File: script/update-delay/base/SetResolverUpdateDelayBase.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "../../actions/base/ActionBase.sol"; import {Network} from "../../../src/Network.sol"; import {INetwork} from "../../../src/interfaces/INetwork.sol"; ⋮---- import {IVetoSlasher} from "@symbioticfi/core/src/interfaces/slasher/IVetoSlasher.sol"; import {ITimelockAction} from "../../actions/interfaces/ITimelockAction.sol"; ⋮---- contract SetResolverUpdateDelayBase is ActionBase, ITimelockAction { ⋮---- function runSchedule() public { ⋮---- function runExecute() public { ⋮---- function runScheduleAndExecute() public { ⋮---- function getTargetAndPayload() public view returns (address target, bytes memory payload) { ```` ## File: script/update-delay/base/UpgradeProxyUpdateDelayBase.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {ActionBase} from "../../actions/base/ActionBase.sol"; import {Network} from "../../../src/Network.sol"; import {INetwork} from "../../../src/interfaces/INetwork.sol"; import {ITimelockAction} from "../../actions/interfaces/ITimelockAction.sol"; ⋮---- import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; ⋮---- contract UpgradeProxyUpdateDelayBase is ActionBase, ITimelockAction { ⋮---- function runSchedule() public { ⋮---- function runExecute() public { ⋮---- function runScheduleAndExecute() public { ⋮---- function getTargetAndPayload() public view returns (address target, bytes memory payload) { ⋮---- function _getProxyAdmin(address proxy) internal view returns (address admin) { ```` ## File: script/update-delay/ArbitraryCallUpdateDelay.s.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./base/ArbitraryCallUpdateDelayBase.sol"; ⋮---- contract ArbitraryCallUpdateDelay is ArbitraryCallUpdateDelayBase { // Configuration constants - UPDATE THESE BEFORE EXECUTING ⋮---- // Address of the Network ⋮---- // Address of the Target to set a delay for (0x0000000000000000000000000000000000000000 means "for any target") ⋮---- // Selector of the Target to set a delay for (0xEEEEEEEE means "for native asset transfers") ⋮---- // New delay for arbitrary operations ⋮---- // Delay for the action to be executed ⋮---- // Optional ⋮---- // Salt for TimelockController operations ⋮---- /** * @notice Schedule an update of the arbitrary call delay through the timelock */ function runS() public { ⋮---- /** * @notice Execute an update of the arbitrary call delay through the timelock */ function runE() public { ⋮---- /** * @notice Schedule and execute an update of the arbitrary call delay through the timelock * @dev It will succeed only if the delay is 0 */ function runSE() public { ```` ## File: script/update-delay/ColdActionsUpdateDelay.s.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {ITimelockAction} from "../actions/interfaces/ITimelockAction.sol"; import {ActionBase} from "../actions/base/ActionBase.sol"; import {SetMiddlewareUpdateDelayBase} from "./base/SetMiddlewareUpdateDelayBase.sol"; import {UpgradeProxyUpdateDelayBase} from "./base/UpgradeProxyUpdateDelayBase.sol"; ⋮---- contract ColdActionsUpdateDelay is ActionBase { // Configuration constants - UPDATE THESE BEFORE EXECUTING ⋮---- // Address of the Network ⋮---- // New delay for cold actions ⋮---- // Delay for the action to be executed ⋮---- // Optional ⋮---- // Salt for TimelockController operations ⋮---- /** * @notice Schedule an update of the cold actions delay through the timelock */ function runS() public { ⋮---- /** * @notice Execute an update of the cold actions delay through the timelock */ function runE() public { ⋮---- /** * @notice Schedule and execute an update of the cold actions delay through the timelock * @dev It will succeed only if the delay is 0 */ function runSE() public { ```` ## File: script/update-delay/DefaultUpdateDelay.s.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./base/DefaultUpdateDelayBase.sol"; ⋮---- contract DefaultUpdateDelay is DefaultUpdateDelayBase { // Configuration constants - UPDATE THESE BEFORE EXECUTING ⋮---- // Address of the Network ⋮---- // New default delay ⋮---- // Delay for the action to be executed ⋮---- // Optional ⋮---- // Salt for TimelockController operations ⋮---- /** * @notice Schedule an update of the default delay through the timelock */ function runS() public { ⋮---- /** * @notice Execute an update of the default delay through the timelock */ function runE() public { ⋮---- /** * @notice Schedule and execute an update of the default delay through the timelock * @dev It will succeed only if the delay is 0 */ function runSE() public { ```` ## File: script/update-delay/HotActionsUpdateDelay.s.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {ITimelockAction} from "../actions/interfaces/ITimelockAction.sol"; import {ActionBase} from "../actions/base/ActionBase.sol"; import {SetMaxNetworkLimitUpdateDelayBase} from "./base/SetMaxNetworkLimitUpdateDelayBase.sol"; import {SetResolverUpdateDelayBase} from "./base/SetResolverUpdateDelayBase.sol"; ⋮---- contract HotActionsUpdateDelay is ActionBase { // Configuration constants - UPDATE THESE BEFORE EXECUTING ⋮---- // Address of the Network ⋮---- // New delay for hot actions ⋮---- // Delay for the action to be executed ⋮---- // Optional ⋮---- // Salt for TimelockController operations ⋮---- /** * @notice Schedule an update of the hot actions delay through the timelock */ function runS() public { ⋮---- /** * @notice Execute an update of the hot actions delay through the timelock */ function runE() public { ⋮---- /** * @notice Schedule and execute an update of the hot actions delay through the timelock * @dev It will succeed only if the delay is 0 */ function runSE() public { ```` ## File: script/update-delay/SetMaxNetworkLimitUpdateDelay.s.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./base/SetMaxNetworkLimitUpdateDelayBase.sol"; ⋮---- contract SetMaxNetworkLimitUpdateDelay is SetMaxNetworkLimitUpdateDelayBase { // Configuration constants - UPDATE THESE BEFORE EXECUTING ⋮---- // Address of the Network ⋮---- // New delay for setMaxNetworkLimit operations ⋮---- // Delay for the action to be executed ⋮---- // Optional ⋮---- // Salt for TimelockController operations ⋮---- /** * @notice Schedule an update of the setMaxNetworkLimit delay through the timelock */ function runS() public { ⋮---- /** * @notice Execute an update of the setMaxNetworkLimit delay through the timelock */ function runE() public { ⋮---- /** * @notice Schedule and execute an update of the setMaxNetworkLimit delay through the timelock * @dev It will succeed only if the delay is 0 */ function runSE() public { ```` ## File: script/update-delay/SetMiddlewareUpdateDelay.s.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./base/SetMiddlewareUpdateDelayBase.sol"; ⋮---- contract SetMiddlewareUpdateDelay is SetMiddlewareUpdateDelayBase { // Configuration constants - UPDATE THESE BEFORE EXECUTING ⋮---- // Address of the Network ⋮---- // New delay for setMiddleware operations ⋮---- // Delay for the action to be executed ⋮---- // Optional ⋮---- // Salt for TimelockController operations ⋮---- /** * @notice Schedule an update of the setMiddleware delay through the timelock */ function runS() public { ⋮---- /** * @notice Execute an update of the setMiddleware delay through the timelock */ function runE() public { ⋮---- /** * @notice Schedule and execute an update of the setMiddleware delay through the timelock * @dev It will succeed only if the delay is 0 */ function runSE() public { ```` ## File: script/update-delay/SetResolverUpdateDelay.s.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./base/SetResolverUpdateDelayBase.sol"; ⋮---- contract SetResolverUpdateDelay is SetResolverUpdateDelayBase { // Configuration constants - UPDATE THESE BEFORE EXECUTING ⋮---- // Address of the Network ⋮---- // New delay for setResolver operations ⋮---- // Delay for the action to be executed ⋮---- // Optional ⋮---- // Salt for TimelockController operations ⋮---- /** * @notice Schedule an update of the setResolver delay through the timelock */ function runS() public { ⋮---- /** * @notice Execute an update of the setResolver delay through the timelock */ function runE() public { ⋮---- /** * @notice Schedule and execute an update of the setResolver delay through the timelock * @dev It will succeed only if the delay is 0 */ function runSE() public { ```` ## File: script/update-delay/UpgradeProxyUpdateDelay.s.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "./base/UpgradeProxyUpdateDelayBase.sol"; ⋮---- contract UpgradeProxyUpdateDelay is UpgradeProxyUpdateDelayBase { // Configuration constants - UPDATE THESE BEFORE EXECUTING ⋮---- // Address of the Network ⋮---- // New delay for upgradeProxy operations ⋮---- // Delay for the action to be executed ⋮---- // Optional ⋮---- // Salt for TimelockController operations ⋮---- /** * @notice Schedule an update of the upgradeProxy delay through the timelock */ function runS() public { ⋮---- /** * @notice Execute an update of the upgradeProxy delay through the timelock */ function runE() public { ⋮---- /** * @notice Schedule and execute an update of the upgradeProxy delay through the timelock * @dev It will succeed only if the delay is 0 */ function runSE() public { ```` ## File: script/utils/sort_errors.py ````python #!/usr/bin/env python3 ⋮---- def _error_sort_key(error_line: str) -> str ⋮---- signature = error_line.strip()[len("error ") :] name = signature.split("(", 1)[0].strip() ⋮---- def _extract_entries(block_text: str) -> List[Tuple[int, int, str, int]] ⋮---- entries: List[Tuple[int, int, str, int]] = [] offset = 0 entry_start: Optional[int] = None last_content_end: Optional[int] = None current_error: Optional[str] = None ⋮---- def finalize() -> None ⋮---- entry_start = None last_content_end = None current_error = None ⋮---- newline_idx = block_text.find("\n", offset) ⋮---- line_end = len(block_text) ⋮---- line_end = newline_idx + 1 line = block_text[offset:line_end] stripped = line.strip() ⋮---- last_content_end = line_end offset = line_end ⋮---- entry_start = offset ⋮---- current_error = line.strip() ⋮---- def _sort_error_block(text: str) -> Tuple[str, bool] ⋮---- marker = "/* ERRORS */" search_pos = 0 changed = False ⋮---- marker_idx = text.find(marker, search_pos) ⋮---- block_start = text.find("\n", marker_idx) ⋮---- cursor = block_start first_block_idx: Optional[int] = None last_block_idx = block_start ⋮---- newline_idx = text.find("\n", cursor) ⋮---- line_end = len(text) ⋮---- line = text[cursor:line_end] ⋮---- cursor = line_end ⋮---- first_block_idx = cursor last_block_idx = line_end ⋮---- search_pos = cursor ⋮---- block_text = text[first_block_idx:last_block_idx] entries = _extract_entries(block_text) ⋮---- search_pos = last_block_idx ⋮---- prefix = block_text[:entries[0][0]] suffix = block_text[entries[-1][1]:] sorted_entries = sorted(entries, key=lambda item: (item[2], item[3])) sorted_chunks = [block_text[start:end].rstrip("\n") for start, end, _, _ in sorted_entries] ⋮---- new_block = prefix + "\n\n".join(sorted_chunks) ⋮---- stripped_suffix = suffix.lstrip("\n") ⋮---- text = text[:first_block_idx] + new_block + text[last_block_idx:] changed = True search_pos = first_block_idx + len(new_block) ⋮---- def sort_errors_in_file(path: Path) ⋮---- original = path.read_text(encoding="utf-8") ⋮---- def iter_solidity_files(targets: Iterable[Path]) -> Iterable[Path] ⋮---- def resolve_targets(path_args: Iterable[str]) -> List[Path] ⋮---- defaults = [Path(name) for name in ("src", "examples") if Path(name).exists()] ⋮---- def sort_from_stdin() -> None ⋮---- errors = sys.stdin.read() sorted_errors = sorted(x.strip() for x in errors.splitlines()) ⋮---- def main() -> int ⋮---- parser = argparse.ArgumentParser( ⋮---- args = parser.parse_args() ⋮---- targets = resolve_targets(args.paths) ```` ## File: script/utils/sort_imports.py ````python #!/usr/bin/env python3 ⋮---- FROM_IMPORT_RE = re.compile(r'from\s+["\']([^"\']+)["\']', re.IGNORECASE | re.MULTILINE) DIRECT_IMPORT_RE = re.compile(r'import\s+["\']([^"\']+)["\']', re.IGNORECASE | re.MULTILINE) WHITESPACE_RE = re.compile(r"\s+") ⋮---- @dataclass class ImportEntry ⋮---- start: int end: int text: str path: str order: int ⋮---- def extract_path(import_text: str) -> str ⋮---- match = FROM_IMPORT_RE.search(import_text) ⋮---- match = DIRECT_IMPORT_RE.search(import_text) ⋮---- def collect_import_entries(lines: Sequence[str]) -> List[ImportEntry] ⋮---- entries: List[ImportEntry] = [] index = 0 ⋮---- stripped = lines[index].lstrip() ⋮---- start = index statement_lines = [lines[index].rstrip()] ⋮---- end = index text = "\n".join(statement_lines) path = extract_path(text) ⋮---- def normalize_import_text(import_text: str) -> str ⋮---- """Flatten whitespace so multi-line imports sort identically to single-line ones.""" compact = WHITESPACE_RE.sub(" ", import_text.strip()) compact = compact.replace("{ ", "{").replace(" }", "}") compact = compact.replace("( ", "(").replace(" )", ")") ⋮---- def classify_import(path: str) -> str ⋮---- normalized = path.lstrip("./") lower = normalized.lower() ⋮---- library = normalized.split("/", 1)[0] ⋮---- def build_sorted_block(entries: Sequence[ImportEntry]) -> List[str] ⋮---- grouped: Dict[str, List[ImportEntry]] = defaultdict(list) ⋮---- ordered_groups: List[List[ImportEntry]] = [] ⋮---- external_groups = sorted( ⋮---- block_text = "\n\n".join("\n".join(entry.text for entry in group) for group in ordered_groups) ⋮---- def sort_imports_in_file(path: Path) -> None ⋮---- original_text = path.read_text(encoding="utf-8") has_trailing_newline = original_text.endswith("\n") lines = original_text.splitlines() ⋮---- entries = collect_import_entries(lines) ⋮---- block_start = entries[0].start block_end = entries[-1].end covered_indexes = set() ⋮---- new_block_lines = build_sorted_block(entries) new_lines = lines[:block_start] + new_block_lines + lines[block_end + 1 :] new_text = "\n".join(new_lines) ⋮---- def iter_solidity_files(targets: Iterable[Path]) -> Iterable[Path] ⋮---- def resolve_targets(path_args: Iterable[str]) -> List[Path] ⋮---- defaults = [Path(name) for name in ("src", "examples") if Path(name).exists()] ⋮---- def main() -> int ⋮---- parser = argparse.ArgumentParser( ⋮---- args = parser.parse_args() ⋮---- targets = resolve_targets(args.paths) ```` ## File: script/DeployNetwork.s.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {DeployNetworkBase} from "./base/DeployNetworkBase.sol"; ⋮---- /** * Deploys Network implementation and a TransparentUpgradeableProxy managed by ProxyAdmin. * Uses CREATE3 for deterministic proxy deployment. * * Configuration is handled entirely by inherited contract. */ contract DeployNetwork is DeployNetworkBase { // Configuration constants - UPDATE THESE BEFORE DEPLOYMENT ⋮---- // Name of the Network ⋮---- // Default minimum delay (will be applied for any action that doesn't have a specific delay yet) ⋮---- // Cold actions delay (a delay that will be applied for major actions like upgradeProxy and setMiddleware) ⋮---- // Hot actions delay (a delay that will be applied for minor actions like setMaxNetworkLimit and setResolver) ⋮---- // Admin address (will become executor, proposer, and default admin by default) ⋮---- // Optional ⋮---- // Metadata URI of the Network ⋮---- // Salt for deterministic deployment ⋮---- function run() public { ```` ## File: script/DeployNetworkForVaults.s.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {DeployNetworkForVaultsBase} from "./base/DeployNetworkForVaultsBase.sol"; ⋮---- /** * Deploys Network implementation and a TransparentUpgradeableProxy managed by ProxyAdmin. * Uses CREATE3 for deterministic proxy deployment. * Also, opt-ins the Network to the given Vault. * * Configuration is handled entirely by inherited contract. */ contract DeployNetworkForVaults is DeployNetworkForVaultsBase { // Configuration constants - UPDATE THESE BEFORE DEPLOYMENT ⋮---- // Name of the Network ⋮---- // Default minimum delay (will be applied for any action that doesn't have a specific delay yet) ⋮---- // Cold actions delay (a delay that will be applied for major actions like upgradeProxy and setMiddleware) ⋮---- // Hot actions delay (a delay that will be applied for minor actions like setMaxNetworkLimit and setResolver) ⋮---- // Admin address (will become executor, proposer, and default admin by default) ⋮---- // Vault address to opt-in to (multiple vaults can be set) ⋮---- // Maximum amount of delegation that network is ready to receive (multiple vaults can be set) ⋮---- // Resolver address (optional, is applied only if VetoSlasher is used) (multiple vaults can be set) ⋮---- // Optional ⋮---- // Subnetwork Identifier (multiple subnetworks can be used, e.g., to have different resolvers for the same network) ⋮---- // Metadata URI of the Network ⋮---- // Salt for deterministic deployment ⋮---- function run() public { ```` ## File: snapshots/gas.txt ```` No files changed, compilation skipped Ran 20 tests for test/Network.t.sol:NetworkTest [PASS] testInitializeCantBeCalledTwice() (gas: 38563) [PASS] test_DirectUpdateDelayReverts() (gas: 31548) [PASS] test_EthTransfer() (gas: 205524) [PASS] test_GetMinDelayInvalidSelectorReverts() (gas: 9433) [PASS] test_Init() (gas: 99556) [PASS] test_PerSelectorDelayOverridesGlobal() (gas: 16091) [PASS] test_ScheduleBatchAndExecute() (gas: 220484) [PASS] test_ScheduleBatchLengthMismatchReverts() (gas: 39546) [PASS] test_ScheduleBatchRevertsIfDelayTooShort() (gas: 59378) [PASS] test_ScheduleRevertsIfDelayTooShort() (gas: 46561) [PASS] test_SetMaxNetworkLimitOnlyMiddleware() (gas: 1324679) [PASS] test_SetMaxNetworkLimitWrongCallerReverts() (gas: 38043) [PASS] test_TryBreakInvariant() (gas: 182261) [PASS] test_UpdateDelayThroughTimelock() (gas: 237398) [PASS] test_UpdateDelayThroughTimelock_RevertInvalidNewDelay() (gas: 115278) [PASS] test_UpdateDelayThroughTimelock_ZeroAddress() (gas: 199124) [PASS] test_UpdateDelayThroughTimelock_ZeroSelector() (gas: 241360) [PASS] test_UpdateMetadataURI() (gas: 49653) [PASS] test_UpdateNameByRoleHolder() (gas: 49702) [PASS] test_UpdateNameWithoutRoleReverts() (gas: 32895) Suite result: ok. 20 passed; 0 failed; 0 skipped; finished in 9.89ms (15.03ms CPU time) ╭----------------------------------+-----------------+--------+--------+--------+---------╮ | src/Network.sol:Network Contract | | | | | | +=========================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------+-----------------+--------+--------+--------+---------| | 0 | 12614 | | | | | |----------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |----------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------+-----------------+--------+--------+--------+---------| | DEFAULT_ADMIN_ROLE | 304 | 304 | 304 | 304 | 1 | |----------------------------------+-----------------+--------+--------+--------+---------| | METADATA_URI_UPDATE_ROLE | 328 | 328 | 328 | 328 | 1 | |----------------------------------+-----------------+--------+--------+--------+---------| | NAME_UPDATE_ROLE | 284 | 284 | 284 | 284 | 1 | |----------------------------------+-----------------+--------+--------+--------+---------| | NETWORK_MIDDLEWARE_SERVICE | 261 | 261 | 261 | 261 | 1 | |----------------------------------+-----------------+--------+--------+--------+---------| | NETWORK_REGISTRY | 304 | 304 | 304 | 304 | 1 | |----------------------------------+-----------------+--------+--------+--------+---------| | PROPOSER_ROLE | 290 | 290 | 290 | 290 | 1 | |----------------------------------+-----------------+--------+--------+--------+---------| | execute | 32943 | 68180 | 70058 | 88034 | 26 | |----------------------------------+-----------------+--------+--------+--------+---------| | executeBatch | 111588 | 111588 | 111588 | 111588 | 1 | |----------------------------------+-----------------+--------+--------+--------+---------| | getMinDelay | 977 | 6865 | 5849 | 12568 | 20 | |----------------------------------+-----------------+--------+--------+--------+---------| | hasRole | 2767 | 2767 | 2767 | 2767 | 4 | |----------------------------------+-----------------+--------+--------+--------+---------| | initialize | 28806 | 474797 | 497097 | 497097 | 21 | |----------------------------------+-----------------+--------+--------+--------+---------| | metadataURI | 3211 | 3211 | 3211 | 3211 | 2 | |----------------------------------+-----------------+--------+--------+--------+---------| | name | 3233 | 3233 | 3233 | 3233 | 2 | |----------------------------------+-----------------+--------+--------+--------+---------| | schedule | 27981 | 55314 | 58120 | 68831 | 32 | |----------------------------------+-----------------+--------+--------+--------+---------| | scheduleBatch | 27163 | 51080 | 44272 | 81805 | 3 | |----------------------------------+-----------------+--------+--------+--------+---------| | setMaxNetworkLimit | 27393 | 47484 | 47484 | 67576 | 2 | |----------------------------------+-----------------+--------+--------+--------+---------| | updateDelay | 23280 | 23280 | 23280 | 23280 | 1 | |----------------------------------+-----------------+--------+--------+--------+---------| | updateMetadataURI | 31848 | 31848 | 31848 | 31848 | 1 | |----------------------------------+-----------------+--------+--------+--------+---------| | updateName | 24770 | 28378 | 28378 | 31987 | 2 | ╰----------------------------------+-----------------+--------+--------+--------+---------╯ Ran 1 test suite in 23.58ms (9.89ms CPU time): 20 tests passed, 0 failed, 0 skipped (20 total tests) ```` ## File: snapshots/sizes.txt ```` No files changed, compilation skipped ╭----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------╮ | Contract | Runtime Size (B) | Initcode Size (B) | Runtime Margin (B) | Initcode Margin (B) | +====================================================================================================================================================================+ | Address | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Bytes | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Checkpoints (lib/core/src/contracts/libraries/Checkpoints.sol) | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Checkpoints (lib/openzeppelin-contracts/contracts/utils/structs/Checkpoints.sol) | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Create2 | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | CreateXWrapper | 2,764 | 2,792 | 21,812 | 46,360 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | ERC1967Proxy | 122 | 934 | 24,454 | 48,218 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | ERC1967Utils | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | ERC4626Math | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Errors | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | FeeOnTransferToken | 1,785 | 3,197 | 22,791 | 45,955 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Math | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Network | 12,358 | 12,550 | 12,218 | 36,602 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Panic | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | ProxyAdmin | 977 | 1,213 | 23,599 | 47,939 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SafeCast | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SafeERC20 | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SignedMath | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | StorageSlot | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Strings | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Subnetwork | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SymbioticCoreBytecode | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SymbioticCoreConstants | 44 | 94 | 24,532 | 49,058 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SymbioticUtils | 80 | 109 | 24,496 | 49,043 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | TimelockController | 6,508 | 7,426 | 18,068 | 41,726 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | TimelockControllerUpgradeable | 7,690 | 7,718 | 16,886 | 41,434 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Token | 1,723 | 3,065 | 22,853 | 46,087 | |----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | TransparentUpgradeableProxy | 1,073 | 3,445 | 23,503 | 45,707 | ╰----------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------╯ ```` ## File: src/interfaces/INetwork.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {ISetMaxNetworkLimitHook} from "./ISetMaxNetworkLimitHook.sol"; ⋮---- /** * @title INetwork * @notice Interface for the Network contract. */ interface INetwork is ISetMaxNetworkLimitHook { /** * @notice Reverts when the calldata length is invalid. */ ⋮---- /** * @notice Reverts when the new delay is non-zero but disabled. */ ⋮---- /** * @notice Reverts when the "recursive" delay update is attempted. */ ⋮---- /** * @notice Reverts when the caller is not the network's middleware. */ ⋮---- /** * @notice The storage of the Network contract. * @param _minDelays The mapping from the id (derived from the target and the selector) to the minimum delay. * @param _isMinDelayEnabled The mapping from the id (derived from the target and the selector) to the minimum delay enabled status. * @param _name The name of the network. * @param _metadataURI The metadata URI of the network. * @custom:storage-location erc7201:symbiotic.storage.Network */ ⋮---- /** * @notice The parameters for the initialization of the Network contract. * @param globalMinDelay The global minimum delay. * @param delayParams The delays. * @param proposers The proposers. * @param executors The executors. * @param name The name of the network. * @param metadataURI The metadata URI of the network. * @param defaultAdminRoleHolder The address of the default admin role holder. * @param nameUpdateRoleHolder The address of the name update role holder. * @param metadataURIUpdateRoleHolder The address of the metadata URI update role holder. */ ⋮---- /** * @notice The delay parameters. * @param target The target address the delay is for. * @param selector The function selector the delay is for. * @param delay The delay value. */ ⋮---- /** * @notice Emitted when the minimum delay is changed. * @param target The target address the delay is for. * @param selector The function selector the delay is for. * @param oldEnabledStatus The old enabled status. * @param oldDelay The old delay value. * @param newEnabledStatus The new enabled status. * @param newDelay The new delay value. */ event MinDelayChange( ⋮---- /** * @notice Emitted when the name is set. * @param name The name of the network. */ event NameSet(string name); ⋮---- /** * @notice Emitted when the metadata URI is set. * @param metadataURI The metadata URI of the network. */ event MetadataURISet(string metadataURI); ⋮---- /** * @notice Returns the role for updating the name. * @return The role for updating the name. */ function NAME_UPDATE_ROLE() external view returns (bytes32); ⋮---- /** * @notice Returns the role for updating the metadata URI. * @return The role for updating the metadata URI. */ function METADATA_URI_UPDATE_ROLE() external view returns (bytes32); ⋮---- /** * @notice Returns the address of the network registry. * @return The address of the network registry. */ function NETWORK_REGISTRY() external view returns (address); ⋮---- /** * @notice Returns the address of the network middleware service. * @return The address of the network middleware service. */ function NETWORK_MIDDLEWARE_SERVICE() external view returns (address); ⋮---- /** * @notice Returns the minimum delay for a given target and calldata. * @param target The target address the delay is for. * @param data The calldata of the function call. * @return The minimum delay for a given target and calldata. */ function getMinDelay(address target, bytes memory data) external view returns (uint256); ⋮---- /** * @notice Returns the name of the network. * @return The name of the network. */ function name() external view returns (string memory); ⋮---- /** * @notice Returns the metadata URI of the network. * @return The metadata URI of the network. */ function metadataURI() external view returns (string memory); ⋮---- /** * @notice Initializes the network. * @param networkInitParams The parameters for the initialization of the network. */ function initialize(NetworkInitParams memory networkInitParams) external; ⋮---- /** * @notice Updates the delay for a given target and selector. * @param target The target address the delay is for. * @param selector The function selector the delay is for. * @param enabled If to enable the delay. * @param newDelay The new delay value. * @dev Can be reached only via scheduled calls. */ function updateDelay(address target, bytes4 selector, bool enabled, uint256 newDelay) external; ⋮---- /** * @notice Updates the name of the network. * @param name The new name. * @dev The caller must have the name update role. */ function updateName(string memory name) external; ⋮---- /** * @notice Updates the metadata URI of the network. * @param metadataURI The new metadata URI. * @dev The caller must have the metadata URI update role. */ function updateMetadataURI(string memory metadataURI) external; ```` ## File: src/interfaces/ISetMaxNetworkLimitHook.sol ```` // SPDX-License-Identifier: MIT ⋮---- /** * @title ISetMaxNetworkLimitHook * @notice Interface for the setMaxNetworkLimit hook. */ interface ISetMaxNetworkLimitHook { /** * @notice Sets the maximum network limit for a delegator. * @param delegator The address of the delegator. * @param subnetworkId The identifier of the subnetwork. * @param maxNetworkLimit The maximum network limit. * @dev The caller must be the network's middleware. */ function setMaxNetworkLimit(address delegator, uint96 subnetworkId, uint256 maxNetworkLimit) external; ```` ## File: src/Network.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {INetwork} from "./interfaces/INetwork.sol"; import {ISetMaxNetworkLimitHook} from "./interfaces/ISetMaxNetworkLimitHook.sol"; ⋮---- import {Bytes} from "@openzeppelin/contracts/utils/Bytes.sol"; import { TimelockControllerUpgradeable } from "@openzeppelin/contracts-upgradeable/governance/TimelockControllerUpgradeable.sol"; ⋮---- import {IBaseDelegator} from "@symbioticfi/core/src/interfaces/delegator/IBaseDelegator.sol"; import {INetworkMiddlewareService} from "@symbioticfi/core/src/interfaces/service/INetworkMiddlewareService.sol"; import {INetworkRegistry} from "@symbioticfi/core/src/interfaces/INetworkRegistry.sol"; ⋮---- /** * @title Network * @notice Contract for network management. * @dev It allows any external watcher to verify if the set delay is sufficient for a given operation (call to some contract's function). * It supports delays for native asset transfers (native transfer is determined as a call with 0xEEEEEEEE selector). * It supports setting delay for (exact target | exact selector) pairs and for (any target | exact selector) pairs. */ contract Network is TimelockControllerUpgradeable, INetwork { ⋮---- /** * @inheritdoc INetwork */ ⋮---- // keccak256(abi.encode(uint256(keccak256("symbiotic.storage.Network")) - 1)) & ~bytes32(uint256(0xff)) ⋮---- // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.TimelockController")) - 1)) & ~bytes32(uint256(0xff)) ⋮---- function _getNetworkStorage() internal pure returns (NetworkStorage storage $) { ⋮---- function _getTimelockControllerStorageOverridden() internal pure returns (TimelockControllerStorage storage $) { ⋮---- function initialize(NetworkInitParams memory initParams) public virtual initializer { ⋮---- function __Network_init(NetworkInitParams memory initParams) internal virtual onlyInitializing { ⋮---- function getMinDelay(address target, bytes memory data) public view virtual returns (uint256) { ⋮---- function name() public view virtual returns (string memory) { ⋮---- function metadataURI() public view virtual returns (string memory) { ⋮---- function updateDelay(address target, bytes4 selector, bool enabled, uint256 newDelay) public virtual { ⋮---- /** * @inheritdoc TimelockControllerUpgradeable */ function schedule( ⋮---- function scheduleBatch( ⋮---- function updateName(string memory name_) public virtual onlyRole(NAME_UPDATE_ROLE) { ⋮---- function updateMetadataURI(string memory metadataURI_) public virtual onlyRole(METADATA_URI_UPDATE_ROLE) { ⋮---- /** * @inheritdoc ISetMaxNetworkLimitHook */ function setMaxNetworkLimit(address delegator, uint96 subnetworkId, uint256 maxNetworkLimit) public virtual { ⋮---- function _updateDelay(address target, bytes4 selector, bool enabled, uint256 newDelay) internal virtual { ⋮---- function _scheduleOverridden(bytes32 id, uint256 delay) internal virtual { ⋮---- function _updateName(string memory name_) internal virtual { ⋮---- function _updateMetadataURI(string memory metadataURI_) internal virtual { ⋮---- function _setMaxNetworkLimit(address delegator, uint96 subnetworkId, uint256 maxNetworkLimit) internal virtual { ⋮---- function _getId(address target, bytes4 selector) internal pure virtual returns (bytes32 id) { ⋮---- function _getMinDelay(address target, bytes4 selector) internal view virtual returns (uint256) { ⋮---- function _getMinDelay(bytes32 id) internal view virtual returns (bool, uint256) { ⋮---- function _getSelector(bytes memory data) internal pure returns (bytes4 selector) { ⋮---- function _getPayload(bytes memory data) internal pure returns (bytes memory payload) { ⋮---- function initialize( uint256, /* minDelay */ address[] memory, /* proposers */ address[] memory, /* executors */ address /* admin */ ```` ## File: test/Network.t.sol ```` // SPDX-License-Identifier: MIT ⋮---- import "forge-std/Test.sol"; ⋮---- import {Network} from "../src/Network.sol"; ⋮---- import {INetwork} from "../src/interfaces/INetwork.sol"; ⋮---- import "@symbioticfi/core/test/integration/SymbioticCoreInit.sol"; import {Subnetwork} from "@symbioticfi/core/src/contracts/libraries/Subnetwork.sol"; import { TimelockControllerUpgradeable } from "@openzeppelin/contracts-upgradeable/governance/TimelockControllerUpgradeable.sol"; ⋮---- contract NetworkTest is SymbioticCoreInit { ⋮---- function setUp() public override { ⋮---- function test_Init() public { ⋮---- function testInitializeCantBeCalledTwice() public { ⋮---- function test_UpdateNameByRoleHolder() public { ⋮---- function test_UpdateNameWithoutRoleReverts() public { ⋮---- function test_UpdateMetadataURI() public { ⋮---- function test_PerSelectorDelayOverridesGlobal() public { ⋮---- function test_ScheduleRevertsIfDelayTooShort() public { ⋮---- function test_UpdateDelayThroughTimelock() public { ⋮---- function test_UpdateDelayThroughTimelock_ZeroAddress() public { ⋮---- function test_UpdateDelayThroughTimelock_ZeroSelector() public { ⋮---- function test_UpdateDelayThroughTimelock_RevertInvalidNewDelay() public { ⋮---- function test_DirectUpdateDelayReverts() public { ⋮---- function test_SetMaxNetworkLimitOnlyMiddleware() public { ⋮---- function test_SetMaxNetworkLimitWrongCallerReverts() public { ⋮---- function test_GetMinDelayInvalidSelectorReverts() public { ⋮---- function test_ScheduleBatchLengthMismatchReverts() public { ⋮---- function test_ScheduleBatchRevertsIfDelayTooShort() public { ⋮---- function test_ScheduleBatchAndExecute() public { ⋮---- function test_EthTransfer() public { ⋮---- function test_TryBreakInvariant() public { ```` ## File: ui/src/abi.ts ````typescript // Minimal ABI for the Network Timelock-compatible contract // Includes OpenZeppelin TimelockController functions/events and custom INetwork bits ⋮---- // Metadata ⋮---- // Custom getMinDelay overrides ⋮---- // Timelock read helpers ⋮---- // Timelock actions ⋮---- // Events // AccessControl events ⋮---- // Global min delay change (OpenZeppelin) ⋮---- // Custom per target/selector delay change (INetwork) ⋮---- // AccessControl functions ⋮---- // Role constant getters (Timelock & custom) ⋮---- export enum OperationState { Unset = 0, Waiting = 1, Ready = 2, Done = 3, } ```` ## File: ui/src/index.css ````css @tailwind base; @tailwind components; @tailwind utilities; ⋮---- /* Optional: smooth fonts & background for nicer look */ :root { html, body { ```` ## File: ui/src/main.tsx ````typescript import React from "react"; ⋮---- import { createRoot } from "react-dom/client"; import TimelockDashboard from "./TimelockDashboard"; import { WagmiProviders } from "./wagmiConfig"; ```` ## File: ui/src/TimelockDashboard.tsx ````typescript import { useEffect, useMemo, useRef, useState } from "react"; import { createPublicClient, decodeEventLog, encodeFunctionData, formatEther, getAddress, Hex, http, parseEther, parseAbiItem, } from "viem"; import type { Abi, AbiFunction, PublicClient } from "viem"; import { useAccount, useChainId, usePublicClient as useWagmiPublicClient, useWalletClient } from "wagmi"; import { wagmiAdapter } from "./wagmiConfig"; import { networkAbi, OperationState } from "./abi"; import { useAppKit } from "@reown/appkit/library/react"; ⋮---- // Constants ⋮---- // Types type ScheduledTx = { target: `0x${string}`; value: bigint; data: Hex; index: bigint; }; ⋮---- type OperationEntry = { id: Hex; predecessor: Hex; salt: Hex; delay: bigint; txs: ScheduledTx[]; timestamp: bigint; state: OperationState; }; ⋮---- type RoleEntry = { role: Hex; name?: string; admin?: Hex | null; members: `0x${string}`[]; }; ⋮---- type DelayEntry = { key: string; target: `0x${string}`; selector: string; enabled: boolean; delay: bigint; }; ⋮---- type BatchRow = { target: string; valueEth: string; data: string; }; ⋮---- type BatchBuilderMode = "raw" | "abi" | "sig"; ⋮---- type ActiveTab = "main" | "acl"; ⋮---- // Utility functions const selectorFromData = (data: string): string => ⋮---- const getLogsChunked = async ( client: PublicClient, params: any, start: bigint, end: bigint, maxSpan: bigint, tokenRef: React.MutableRefObject, myToken: number, ) => ⋮---- const isArchiveRpcError = (error: any): boolean => ⋮---- const handleError = (error: any, context: string) => ⋮---- // Could add user-facing error notifications here ⋮---- // Custom hooks const useRpcChainId = (rpcUrl: string, effectivePublicClient: PublicClient | null) => ⋮---- async function fetchChainId() ⋮---- // Loading states ⋮---- const [executing, setExecuting] = useState(""); // id being executed ⋮---- // Batch builder state ⋮---- // Batch schedule state ⋮---- // Configuration state ⋮---- // Token refs for cancellation ⋮---- // Access Control state ⋮---- // Unified loading state across sections ⋮---- // Extracted Access Control loader for unified Load const loadAccessControl = async () => ⋮---- // @ts-ignore ⋮---- // @ts-ignore ⋮---- // @ts-ignore ⋮---- // @ts-ignore ⋮---- const tryRead = async (fn: string, name: string) => ⋮---- // Unified load and cancel controls ⋮---- const detectDeploymentBlock = async () => ⋮---- // Derive chainId from custom RPC when wallet is not connected ⋮---- // Explorer link for manual deployment block lookup (uses configured chains) ⋮---- const readHeader = async () => ⋮---- const loadDelays = async () => ⋮---- // Decode only the custom MinDelayChange by trying both event defs and accepting the address+bytes4 one ⋮---- // Two possible overloads; we need the one with address + bytes4 indexed ⋮---- // Sort: enabled first, then by target, then selector ⋮---- const loadOperations = async () => ⋮---- // Fetch CallScheduled logs ⋮---- // Fetch CallSalt logs ⋮---- // @ts-ignore viem narrows via event ⋮---- // Group scheduled by id ⋮---- // @ts-ignore event typing from viem ⋮---- // @ts-ignore ⋮---- // @ts-ignore ⋮---- // @ts-ignore ⋮---- // @ts-ignore ⋮---- // @ts-ignore ⋮---- // @ts-ignore ⋮---- // Sort txs by index and fill status/timestamp from contract ⋮---- // ignore ⋮---- // Sort entries: Ready first, then Waiting by timestamp, then Done ⋮---- const prio = (x: OperationEntry) ⋮---- // Single schedule removed ⋮---- const onScheduleBatch = async () => ⋮---- // Prompt user to connect if not connected ⋮---- const onExecute = async (entry: OperationEntry) => ⋮---- // Single builder removed ⋮---- // Batch builder helpers const buildBatchCalldataFromAbi = () => ⋮---- const buildBatchCalldataFromSignature = () => ⋮---- const computeBatchMinDelay = async () => ⋮---- // Keep required min delay up to date when inputs change ⋮---- onChange= ⋮---- setDetectingFromBlock(false); ⋮---- onClick= ⋮---- ```` ## File: ui/src/vite-env.d.ts ````typescript /// ```` ## File: ui/src/wagmiConfig.tsx ````typescript import type { ReactNode } from "react"; import { WagmiProvider } from "wagmi"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { mainnet, sepolia, holesky, hoodi } from "@reown/appkit/networks"; import { createAppKit } from "@reown/appkit/react"; import { WagmiAdapter } from "@reown/appkit-adapter-wagmi"; ⋮---- // 0. Setup queryClient ⋮---- // 1. Get projectId from https://dashboard.reown.com ⋮---- // 2. Create a metadata object ⋮---- // 4. Create Wagmi Adapter ⋮---- // 5. Create modal ⋮---- export function AppKitProvider( ```` ## File: ui/eslint.config.js ````javascript export default tseslint.config( ```` ## File: ui/index.html ````html Network Dashboard
```` ## File: ui/postcss.config.cjs ```` // Resolve config path regardless of CWD ```` ## File: ui/tailwind.config.cjs ```` /** @type {import('tailwindcss').Config} */ ⋮---- // Absolute paths so Tailwind finds files regardless of CWD content: [path.join(__dirname, "index.html"), path.join(__dirname, "src/**/*.{ts,tsx,js,jsx}")], ```` ## File: ui/tsconfig.app.json ````json { "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true, "moduleResolution": "bundler", "allowImportingTsExtensions": true, "isolatedModules": true, "moduleDetection": "force", "noEmit": true, "jsx": "react-jsx", "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, "include": ["src"] } ```` ## File: ui/tsconfig.json ````json { "files": [], "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }] } ```` ## File: ui/tsconfig.node.json ````json { "compilerOptions": { "target": "ES2022", "lib": ["ES2023"], "module": "ESNext", "skipLibCheck": true, "moduleResolution": "bundler", "allowImportingTsExtensions": true, "isolatedModules": true, "moduleDetection": "force", "noEmit": true, "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, "include": ["vite.config.mts"] } ```` ## File: ui/vite.config.mts ```` import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; ```` ## File: .gitignore ```` # Ignore node_modules and dependencies node_modules *.log yarn-error.log # Ignore build outputs dist out **/__pycache__ # Ignore environment files .env # Ignore version control files docs/autogen/.git docs/autogen/.gitignore # Ignore editor and OS-specific files *.swp *.DS_Store Thumbs.db # Ignore IDE-specific files .idea/ .vscode/ cache/ out/ broadcast/ deployments/ script/logs.txt lcov.info /target /coverage /report .password ```` ## File: .gitmodules ```` [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts url = https://github.com/OpenZeppelin/openzeppelin-contracts [submodule "lib/openzeppelin-contracts-upgradeable"] path = lib/openzeppelin-contracts-upgradeable url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable [submodule "lib/core"] path = lib/core url = https://github.com/symbioticfi/core ```` ## File: .nvmrc ```` 20 ```` ## File: .pre-commit-config.yaml ````yaml repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: mixed-line-ending args: ["--fix=lf"] exclude: "^(docs/autogen|snapshots/)" - id: trailing-whitespace exclude: "^(docs/autogen|snapshots/)" - id: end-of-file-fixer exclude: "^(docs/autogen|snapshots/)" - id: check-merge-conflict - id: check-json exclude: "^(docs/autogen|snapshots/)" - id: check-yaml - repo: local hooks: - id: sort-imports name: Sort imports description: Normalize and sort import statements in Solidity files language: system entry: python3 script/utils/sort_imports.py files: "^(src|examples)/.*\\.sol$" pass_filenames: true - id: sort-errors name: Sort solidity errors description: Alphabetize error declarations in Solidity files language: system entry: python3 script/utils/sort_errors.py files: "^(src|examples)/.*\\.sol$" pass_filenames: true - id: format name: Format solidity code description: Format solidity code with `forge fmt` language: system entry: forge fmt files: '\.sol$' exclude: "^lib/" pass_filenames: true - id: doc name: Generate documentation description: Generate docs with `forge doc` language: system entry: bash -lc 'forge build; rm -rf docs/autogen; forge doc -b -o docs/autogen' pass_filenames: false - id: forge-snapshots name: Update forge snapshots language: system entry: bash -lc 'mkdir -p snapshots; forge build; forge build --sizes | tee snapshots/sizes.txt >/dev/null; forge test --isolate --gas-report | tee snapshots/gas.txt >/dev/null' pass_filenames: false - repo: https://github.com/pre-commit/mirrors-prettier rev: v3.1.0 hooks: - id: prettier name: Format non-solidity files with prettier exclude: "^(docs/autogen|snapshots/)" ```` ## File: .prettierignore ```` foundry.toml out lib/ cache/ docs/autogenerated *.sol deployments/ snapshots/ ```` ## File: .prettierrc ```` { "printWidth": 120, "tabWidth": 2, "useTabs": false, "singleQuote": false, "bracketSpacing": true, "trailingComma": "all", "overrides": [ { "files": "*.sol", "options": { "printWidth": 120, "tabWidth": 4, "useTabs": false, "singleQuote": false, "bracketSpacing": false } }, { "files": "*.json", "options": { "tabWidth": 4 } } ] } ```` ## File: codecov.yml ````yaml codecov: require_ci_to_pass: true coverage: precision: 2 round: down range: "70...100" status: project: default: # basic target: auto threshold: 0% base: auto # advanced if_no_uploads: error if_not_found: success if_ci_failed: error only_pulls: false patch: default: # basic target: auto threshold: 0% base: auto # advanced if_no_uploads: error if_not_found: success if_ci_failed: error only_pulls: false parsers: gcov: branch_detection: conditional: true loop: true method: false macro: false comment: layout: "reach,diff,flags,files,footer" behavior: default require_changes: false require_base: false require_head: true ignore: - "script" - "test" - "src/test" ```` ## File: CONTRIBUTING.md ````markdown # Contributing - [Install](#install) - [Pre-commit Hooks](#pre-commit-hooks) - [Requirements for merge](#requirements-for-merge) - [Branching](#branching) - [Main](#main) - [Audit](#audit) - [Code Practices](#code-practices) - [Code Style](#code-style) - [Solidity Versioning](#solidity-versioning) - [Interfaces](#interfaces) - [NatSpec \& Comments](#natspec--comments) - [Testing](#testing) - [Best Practices](#best-practices) - [IR Compilation](#ir-compilation) - [Gas Metering](#gas-metering) - [Deployment](#deployment) - [Bytecode Hash](#bytecode-hash) - [Dependency Management](#dependency-management) - [Releases](#releases) ## Install Follow these steps to set up your local environment for development: - [Install foundry](https://book.getfoundry.sh/getting-started/installation) - Install dependencies: `forge install` - [Install pre-commit](https://pre-commit.com/#installation) - Install pre commit hooks: `pre-commit install` ## Pre-commit Hooks Follow the [installation steps](#install) to enable pre-commit hooks. To ensure consistency in our formatting `pre-commit` is used to check whether code was formatted properly and the documentation is up to date. Whenever a commit does not meet the checks implemented by pre-commit, the commit will fail and the pre-commit checks will modify the files to make the commits pass. Include these changes in your commit for the next commit attempt to succeed. On pull requests the CI checks whether all pre-commit hooks were run correctly. This repo includes the following pre-commit hooks that are defined in the `.pre-commit-config.yaml`: - `mixed-line-ending`: This hook ensures that all files have the same line endings (LF). - `trailing-whitespace`: Strips trailing spaces from lines so that diffs remain clean and editors don't introduce noise. - `end-of-file-fixer`: Ensures every file ends with a single newline and removes extra blank lines at the end of files. - `check-merge-conflict`: Fails when Git merge conflict markers are present to avoid committing unresolved conflicts. - `check-json`: Validates JSON files and fails fast on malformed syntax. - `check-yaml`: Parses YAML files to verify they are syntactically valid. - `sort-imports`: Normalises and sorts imports according to the rules mentioned in the [Code Style](#code-style) below. - `sort-errors`: Sorts errors according to the rules mentioned in the [Code Style](#code-style) below. - `format`: This hook uses `forge fmt` to format all Solidity files. - `doc`: This hook uses `forge doc` to generate the Solidity documentation. Commit the generated files whenever the documentation changes. - `prettier`: All remaining files are formatted using prettier. ## Requirements for merge In order for a PR to be merged, it must pass the following requirements: - All commits within the PR must be signed - CI must pass (tests, linting, etc.) - New features must be merged with associated tests - Bug fixes must have a corresponding test that fails without the fix - The PR must be approved by at least one maintainer ## Branching This section outlines the branching strategy of this repo. ### Main The main branch is supposed to reflect the deployed state on all networks, if not indicated otherwise inside the README. Only audited code should be merged into main. Сommits from dev branches should be merged into the main branch using a regular merge strategy. The commit messages should follow [the Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/). ### Audit Before an audit, the code should be frozen on a branch dedicated to the audit with the naming convention `audit/`. Each fix in response to an audit finding should be developed as a separate commit. The commit message should look similar to `fix: - `. ## Code Practices ### Code Style The repo follows the official [Solidity Style Guide](https://docs.soliditylang.org/en/latest/style-guide.html). In addition to that, this repo also borrows the following rules from [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/GUIDELINES.md#solidity-conventions): - Internal or private state variables or functions should have an underscore prefix. ```solidity contract TestContract { uint256 private _privateVar; uint256 internal _internalVar; function _testInternal() internal { ... } function _testPrivate() private { ... } } ``` - Naming collisions should be avoided using a single trailing underscore. ```solidity contract TestContract { uint256 public foo; constructor(uint256 foo_) { foo = foo_; } } ``` - Interface names should have a capital I prefix. ```solidity interface IERC777 { ``` - Contracts not intended to be used standalone should be marked abstract, so they are required to be inherited by other contracts. ```solidity abstract contract AccessControl is ..., { ``` - Unchecked arithmetic blocks should contain comments explaining why overflow is guaranteed not to happen or is permissible. If the reason is immediately apparent from the line above the unchecked block, the comment may be omitted. Also, such exceptions/additions exist: - Functions should be grouped according to their visibility and ordered: 1. constructor 2. external 3. public 4. internal 5. private 6. receive function (if exists) 7. fallback function (if exists) - Each contract should be virtually divided into sections by using such separators: 1. /\* CONSTANTS \*/ 2. /\* IMMUTABLES \*/ 3. /\* STATE VARIABLES \*/ 4. /\* MODIFIERS \*/ 5. /\* CONSTRUCTOR \*/ 6. /\* EXTERNAL FUNCTIONS \*/ 7. /\* PUBLIC FUNCTIONS \*/ 8. /\* INTERNAL FUNCTIONS \*/ 9. /\* PRIVATE FUNCTIONS \*/ 10. /\* RECEIVE FUNCTION \*/ 11. /\* FALLBACK FUNCTION \*/ - Each interface should be virtually divided into sections by using such separators: 1. /\* ERRORS \*/ 2. /\* STRUCTS \*/ 3. /\* EVENTS \*/ 4. /\* FUNCTIONS \*/ - Do not use external and private visibilities in most cases. - Events should generally be emitted immediately after the state change that they represent, and should be named the same as the function's name. Some exceptions may be made for gas efficiency if the result doesn't affect the observable ordering of events. ```solidity function _burn(address who, uint256 value) internal { super._burn(who, value); emit Burn(who, value); } ``` - Custom errors should be used whenever possible. The naming should be concise and easy to read. - Imports should be divided into separate groups and ordered alphabetically ascending inside each group: 1. contracts 2. libraries 3. interfaces 4. external files separately ```solidity import {NetworkManager} from "../base/NetworkManager.sol"; import {OzEIP712} from "../base/OzEIP712.sol"; import {PermissionManager} from "../base/PermissionManager.sol"; import {Checkpoints} from "../../libraries/structs/Checkpoints.sol"; import {KeyTags} from "../../libraries/utils/KeyTags.sol"; import {ISettlement} from "../interfaces/modules/settlement/ISettlement.sol"; import {ISigVerifier} from "../interfaces/modules/settlement/sig-verifiers/ISigVerifier.sol"; import {StaticDelegateCallable} from "@symbioticfi/core/src/contracts/common/StaticDelegateCallable.sol"; import {Subnetwork} from "@symbioticfi/core/src/contracts/libraries/Subnetwork.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; ``` - In case of comparison with `msg.sender` or `tx.origin`, these keywords should be on the right side of the inequality. ```solidity modifier onlyOwner() internal { if (owner != msg.sender) { revert NotOwner(); } } ``` - Errors should be ordered alphabetically ascending. ```solidity error InsufficientFunds(); error NoAccess(); error NotOwner(); ``` ### Solidity Versioning Contracts that are meant to be deployed should have an explicit version set in the `pragma` statement. ```solidity pragma solidity 0.8.X; ``` Abstract contracts, libraries and interfaces should use the caret (`^`) range operator to specify the version range to ensure better compatibility. ```solidity pragma solidity ^0.X.0; ``` Libraries and abstract contracts using functionality introduced in newer versions of Solidity can use caret range operators with higher path versions (e.g., `^0.8.24` when using transient storage opcodes). For interfaces, it should be considered to use the greater than or equal to (`>=`) range operator to ensure better compatibility with future versions of Solidity. ### Interfaces Every contract MUST implement its corresponding interface that includes all externally callable functions, errors and events. ### NatSpec & Comments Interfaces should be the entry point for all contracts. When exploring a contract within the repository, the interface MUST contain all relevant information to understand the functionality of the contract in the form of NatSpec comments. This includes all externally callable functions, structs, errors and events. The NatSpec documentation MUST be added to the functions, structs, errors and events within the interface. This allows a reader to understand the functionality of a function before moving on to the implementation. The implementing functions MUST point to the NatSpec documentation in the interface using `@inheritdoc`. Internal and private functions shouldn't have NatSpec documentation except for `@dev` comments, whenever more context is needed. Additional comments within a function should only be used to give more context to more complex operations; otherwise, the code should be kept readable and self-explanatory. NatSpec comments in contracts should use a triple slash (`///`) to bring less noise to the implementation, while libraries and interfaces should use `/* */` wrappers. The comments should respect the following rules: - For read functions: `@notice Returns <...>` - For write functions: `@notice ` - For structs: `@notice ` - For errors: `@notice Raised when <...>` - For events: `@notice Emitted when <...>` Each contract/library/interface should have a title comment that should follow such a structure: 1. `@title ` (e.g., `Vault`) 2. `@notice Contract/Library/Interface for <...>.` - also, other variations are possible, e.g.: - `@notice Interface for the Vault contract.` - `@notice Base contract for <...>.` - `@notice Library-logic for <...>.` 3. `@dev <...>` (optional) ## Testing The following testing practices should be followed when writing unit tests for new code. All functions, lines and branches should be tested to result in 100% testing coverage. Fuzz parameters and conditions whenever possible. Extremes should be tested in dedicated edge case and corner case tests. Invariants should be tested in dedicated invariant tests. Differential testing should be used to compare assembly implementations with implementations in Solidity or testing alternative implementations against existing Solidity or non-Solidity code using ffi. New features must be merged with associated tests. Bug fixes should have a corresponding test that fails without the bug fix. ### Best Practices Best practices and naming conventions should be followed as outlined in the [Foundry Book](https://getfoundry.sh/forge/tests/overview). ### IR Compilation All contracts and tests should be compilable without IR whenever possible. ### Gas Metering Gas for function calls should be metered using the built-in `vm.snapshotGasLastCall` function in forge. To meter across multiple calls `vm.startSnapshotGas` and `vm.stopSnapshotGas` can be used. Tests that measure gas should be annotated with `/// forge-config: default.isolate = true` and not be fuzzed to ensure that the gas snapshot is accurate and consistent for CI verification. All external functions should have a gas snapshot test, and diverging paths within a function should have appropriate gas snapshot tests. For more information on gas metering, see the [Forge cheatcodes reference](https://getfoundry.sh/reference/cheatcodes/gas-snapshots/#snapshotgas-cheatcodes). ### Bytecode Hash Bytecode hash should be set to `none` in the `foundry.toml` file to ensure that the bytecode is consistent. ## Dependency Management The preferred way to manage dependencies is using [`forge install`](https://book.getfoundry.sh/forge/dependencies). This ensures that your project uses the correct versions and structure for all external libraries. Also, `npm` and `soldeer` packages should be published to increase coverage of different use-cases. ## Releases Every deployment and change made to contracts after deployment should be accompanied by a tag and release on GitHub. ```` ## File: foundry.lock ```` { "lib/core": { "tag": { "name": "v1.0.3", "rev": "74c0c6fbd5531a6065cdafdab07840b04a0d2039" } }, "lib/forge-std": { "tag": { "name": "v1.10.0", "rev": "8bbcf6e3f8f62f419e5429a0bd89331c85c37824" } }, "lib/openzeppelin-contracts": { "tag": { "name": "v5.4.0", "rev": "c64a1edb67b6e3f4a15cca8909c9482ad33a02b0" } }, "lib/openzeppelin-contracts-upgradeable": { "tag": { "name": "v5.4.0", "rev": "e725abddf1e01cf05ace496e950fc8e243cc7cab" } } } ```` ## File: foundry.toml ````toml [profile.default] evm_version = "prague" solc = "0.8.28" optimizer = true optimizer_runs = 200 via_ir = false bytecode_hash = "none" src = "src" out = "out" libs = ["lib"] fs_permissions = [{ access = "read-write", path = "./"}] gas_reports = ["*"] gas_limit = "18446744073709551615" dynamic_test_linking = true ignored_warnings_from = ["test/","script/"] ffi = true [rpc_endpoints] mainnet = "${ETH_RPC_URL}" hoodi = "${ETH_RPC_URL_HOODI}" holesky = "${ETH_RPC_URL_HOLESKY}" sepolia = "${ETH_RPC_URL_SEPOLIA}" [fmt] bracket_spacing = false int_types = "long" line_length = 120 multiline_func_header = "attributes_first" number_underscore = "thousands" quote_style = "double" tab_width = 4 single_line_statement_blocks = "preserve" sort_imports = false contract_new_lines = false override_spacing = false hex_underscore = "preserve" wrap_comments = false [lint] lint_on_build = false exclude_lints = ["asm-keccak256","mixed-case-function","mixed-case-variable","pascal-case-struct","screaming-snake-case-const"] ignore = ["test/**/*.sol","script/**/*.sol","src/interfaces/**/*.sol"] additional_compiler_profiles = [ { name = "test", via_ir = false, optimizer = false } ] compilation_restrictions = [ { paths = "test/**", via_ir = false, optimizer = false } ] [profile.default.fuzz] runs = 1000 max_test_rejects = 262144 [profile.pr.fuzz] runs = 10000 max_test_rejects = 262144 [profile.ci.fuzz] runs = 100000 max_test_rejects = 262144 [profile.debug] via_ir = false optimizer_runs = 200 fuzz.runs = 100 # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options ```` ## File: LICENSE ```` MIT License Copyright (c) 2024 Symbiotic Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ```` ## File: package.json ````json { "name": "@symbioticfi/network", "version": "1.0.0-rc.1", "description": "Symbiotic Network contract provides a flexible and secure mechanism to manage network operations with customizable delays.", "homepage": "https://symbiotic.fi/network", "bugs": "https://github.com/symbioticfi/network/issues", "license": "MIT", "author": "Symbiotic Team", "files": [ "examples/**/*", "src/**/*", "script/**/*", "test/mocks/**/*" ], "repository": { "type": "git", "url": "https://github.com/symbioticfi/network.git" }, "keywords": [ "solidity", "ethereum", "smart", "contracts", "security" ], "scripts": { "dev": "vite ui", "build": "vite build ui", "preview": "vite preview ui", "typecheck": "tsc --noEmit" }, "dependencies": { "@symbioticfi/core": "1.0.3", "@openzeppelin/contracts": "5.4.0", "@openzeppelin/contracts-upgradeable": "5.4.0", "@reown/appkit": "^1.8.4", "@reown/appkit-adapter-wagmi": "^1.8.4", "@tanstack/react-query": "^5.87.1", "react": "^18.3.1", "react-dom": "^18.3.1", "viem": "^2.37.5", "wagmi": "^2.16.9" }, "devDependencies": { "@types/node": "^20.16.11", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.1", "autoprefixer": "^10.4.20", "postcss": "^8.4.41", "tailwindcss": "^3.4.10", "typescript": "^5.5.4", "vite": "^5.4.2" }, "engines": { "node": ">=18" } } ```` ## File: README.md ````markdown **[Symbiotic Protocol](https://symbiotic.fi) is an extremely flexible and permissionless shared security system.** This repository contains a default Network contract and tooling to manage it. Basically, a Network contract is an [OZ's TimelockController](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/governance/TimelockController.sol) with additional functionality to define delays for either (exact target | exact selector) or (any target | exact selector) pairs. [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/symbioticfi/network) [![codecov](https://codecov.io/github/symbioticfi/network/graph/badge.svg?token=ZSNR9UBS59)](https://codecov.io/github/symbioticfi/network) ## Documentation - [What is Network?](https://docs.symbiotic.fi/modules/counterparties/networks) - [What are Resolvers?](https://docs.symbiotic.fi/modules/counterparties/resolvers) - [Network Registration](https://docs.symbiotic.fi/modules/registries#network-registration-process) ## Usage ### Dependencies - Git ([installation](https://git-scm.com/downloads)) - foundry ([installation](https://getfoundry.sh/introduction/installation/)) - npm ([installation](https://nodejs.org/en/download/)) ### Prerequisites **Clone the repository** ```bash git clone --recurse-submodules https://github.com/symbioticfi/network.git ``` **Install dependencies** ```bash npm install ``` ### Deploy Your Network - If you need a pure Network deployment Open [DeployNetwork.s.sol](./script/DeployNetwork.s.sol), you will see config like this: ```solidity // Name of the Network string NAME = "My Network"; // Default minimum delay (will be applied for any action that doesn't have a specific delay yet) uint256 DEFAULT_MIN_DELAY = 3 days; // Cold actions delay (a delay that will be applied for major actions like upgradeProxy and setMiddleware) uint256 COLD_ACTIONS_DELAY = 14 days; // Hot actions delay (a delay that will be applied for minor actions like setMaxNetworkLimit and setResolver) uint256 HOT_ACTIONS_DELAY = 0; // Admin address (will become executor, proposer, and default admin by default) address ADMIN = 0x0000000000000000000000000000000000000000; // Optional // Metadata URI of the Network string METADATA_URI = ""; // Salt for deterministic deployment bytes11 SALT = "SymNetwork"; ``` Edit needed fields, and execute the script via: ```bash forge script script/DeployNetwork.s.sol --rpc-url --private-key --etherscan-api-key --broadcast --verify ``` - If you need a Network deployment for already-deployed Vaults Open [DeployNetworkForVaults.s.sol](./script/DeployNetworkForVaults.s.sol), you will see config like this: ```solidity // Name of the Network string NAME = "My Network"; // Default minimum delay (will be applied for any action that doesn't have a specific delay yet) uint256 DEFAULT_MIN_DELAY = 3 days; // Cold actions delay (a delay that will be applied for major actions like upgradeProxy and setMiddleware) uint256 COLD_ACTIONS_DELAY = 14 days; // Hot actions delay (a delay that will be applied for minor actions like setMaxNetworkLimit and setResolver) uint256 HOT_ACTIONS_DELAY = 0; // Admin address (will become executor, proposer, and default admin by default) address ADMIN = 0x0000000000000000000000000000000000000000; // Vault address to opt-in to (multiple vaults can be set) address[] VAULTS = [0x0000000000000000000000000000000000000000]; // Maximum amount of delegation that network is ready to receive (multiple vaults can be set) uint256[] MAX_NETWORK_LIMITS = [0]; // Resolver address (optional, is applied only if VetoSlasher is used) (multiple vaults can be set) address[] RESOLVERS = [0x0000000000000000000000000000000000000000]; // Optional // Subnetwork Identifier (multiple subnetworks can be used, e.g., to have different resolvers for the same network) uint96 SUBNETWORK_ID = 0; // Metadata URI of the Network string METADATA_URI = ""; // Salt for deterministic deployment bytes11 SALT = "SymNetwork"; ``` Edit needed fields, and execute the script via: ```bash forge script script/DeployNetworkForVaults.s.sol --rpc-url --private-key --etherscan-api-key --broadcast --verify ``` In the console, you will see logs like these: ```bash Deployed network network:0x90F545649eDA7a2083bA30ECC5C21335d030ae1d proxyAdminContract:0x79d771aeC770C936E05E51fA8e68f019a373f9b1 newImplementation:0x3C00C0ef1B1be4dFFeF07dB148bb5fbF31277091 salt:0x5465737433000000000000000000000000000000000000000000000000000000 Opted network into vault network:0x90F545649eDA7a2083bA30ECC5C21335d030ae1d vault:0x49fC19bAE549e0b5F99B5b42d7222Caf09E8d2a1 subnetworkId:0 maxNetworkLimit:1000 resolver:0xbf616b04c463b818e3336FF3767e61AB44103243 ``` ### Manage Your Network There are 5 predefined [action-scripts](./script/actions/), that you can use from the start: - [SetMaxNetworkLimit](./script/actions/SetMaxNetworkLimit.s.sol) - set new [maximum network limit](https://docs.symbiotic.fi/modules/registries#3-network-to-vault-opt-in) for the vault - [SetResolver](./script/actions/SetResolver.s.sol) - set a new [resolver](https://docs.symbiotic.fi/modules/counterparties/resolvers) for the vault (only if the vault uses [VetoSlasher](https://docs.symbiotic.fi/modules/vault/slashing#1-vetoslasher)) - [SetMiddleware](./script/actions/SetMiddleware.s.sol) - set a new middleware - [UpgradeProxy](./script/actions/UpgradeProxy.s.sol) - upgrade the proxy (network itself) - [ArbitraryCall](./script/actions/ArbitraryCall.s.sol) - make a call to any contract with any data Interaction with different actions is similar; let's consider [SetMaxNetworkLimit](./script/actions/SetMaxNetworkLimit.s.sol) as an example: 1. Open [SetMaxNetworkLimit.s.sol](./script/actions/SetMaxNetworkLimit.s.sol), you will see config like this: ```solidity // Address of the Network address NETWORK = 0x0000000000000000000000000000000000000000; // Address of the Vault address VAULT = 0x0000000000000000000000000000000000000000; // Maximum amount of delegation that network is ready to receive uint256 MAX_NETWORK_LIMIT = 0; // Delay for the action to be executed uint256 DELAY = 0; // Optional // Subnetwork Identifier (multiple subnetworks can be used, e.g., to have different max network limits for the same network) uint96 SUBNETWORK_IDENTIFIER = 0; // Salt for TimelockController operations bytes32 SALT = "SetMaxNetworkLimit"; ``` 2. Edit needed fields, and choose an operation: - `runS()` - schedule an action - `runE` - execute an action - `runSE()` - schedule and execute an action (possible only if a delay for the needed action is zero) 3. Execute the operation: - If you use an EOA and want to execute the script: ```bash forge script script/actions/SetMaxNetworkLimit.s.sol:SetMaxNetworkLimit --sig "runS()" --rpc-url --private-key --broadcast ``` - If you use a Safe multisig and want to get a transaction calldata: ```bash forge script script/actions/SetMaxNetworkLimit.s.sol:SetMaxNetworkLimit --sig "runS()" --rpc-url --sender --unlocked ``` In the logs, you will see `callData` field like this: ```bash callData:0x01d5062a00000000000000000000000025ed2ee6e295880326bdeca245ee4d8b72c8f103000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000005365744d61784e6574776f726b4c696d697400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004423f752d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b6700000000000000000000000000000000000000000000000000000000 ``` In Safe->TransactionBuilder, you should: - enable "Custom data" - enter **Network's address** as a target address - use the `callData` (e.g., `0x01d5062a0000000000000000000000...`) received earlier as a `Data (Hex encoded)` ### Update Delays Any action that can be made by the Network is protected by the corresponding delay (which can be any value from zero to infinity). We provide "update delay" scripts for actions mentioned above, and also some additional ones: - [SetMaxNetworkLimitUpdateDelay](./script/update-delay/SetMaxNetworkLimitUpdateDelay.s.sol) - [SetResolverUpdateDelay](./script/update-delay/SetResolverUpdateDelay.s.sol) - [SetMiddlewareUpdateDelay](./script/update-delay/SetMiddlewareUpdateDelay.s.sol) - [UpgradeProxyUpdateDelay](./script/update-delay/UpgradeProxyUpdateDelay.s.sol) - [HotActionsUpdateDelay](./script/update-delay/HotActionsUpdateDelay.s.sol) - update a delay for [SetMiddlewareUpdateDelay](./script/update-delay/SetMiddlewareUpdateDelay.s.sol) and [SetResolverUpdateDelay](./script/update-delay/SetResolverUpdateDelay.s.sol) - [ColdActionsUpdateDelay](./script/update-delay/ColdActionsUpdateDelay.s.sol) - update a delay for [SetMaxNetworkLimitUpdateDelay](./script/update-delay/SetMaxNetworkLimitUpdateDelay.s.sol) and [UpgradeProxyUpdateDelay](./script/update-delay/UpgradeProxyUpdateDelay.s.sol) - [DefaultUpdateDelay](./script/update-delay/DefaultUpdateDelay.s.sol) - update a delay for unconstrained actions - [ArbitraryUpdateDelay](./script/update-delay/ArbitraryCallUpdateDelay.s.sol) - update a delay for an arbitrary call: - set a delay for the exact target address and the exact selector - set a delay for any target address and the exact selector (by setting target address to `0x0000000000000000000000000000000000000000`) For example usage of similar scripts see ["Manage Your Network"](./README.md#manage-your-network). ### Dashboard **Note: work-in-progress, use with caution** `Network` contract inherits OpenZeppelin's `TimelockController`, while `TimelockController` inherits `AccessControl`. The similarity between `TimelockController` and `AccessControl` contracts' logic is that it is not possible to adequately determine their state (e.g., statuses of operations or holders of roles) using only the current chain's state via RPC calls. Hence, we provide a [Network Dashboard](./ui/) which allows you to: - Get delays for all operations - Get holders of any role - Get scheduled/executed operations - Schedule/execute arbitrary actions Run the command: ```bash npm run dev ``` ### Build, Test, and Format ``` forge build forge test forge fmt ``` **Configure environment** Create `.env` based on the template: ``` ETH_RPC_URL= ETHERSCAN_API_KEY= ``` ## Security Security audits can be found [here](./audits). ```` ## File: remappings.txt ```` @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ @openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ @symbioticfi/core/=lib/core/ ````