All audits

Web3 · Smart contract audit

Syndicate

softstack has completed 7 smart contract audits for Syndicate between August 2022 and December 2025, reporting 47 findings in total (1 critical, 6 high, 18 medium, 14 low and 8 informational). Every report is published in the softstack public audit archive.

Key facts

Client
Syndicate
Audits completed
7
First audited
Latest audit
Type
Smart contract audit
Chains
Arbitrum, Optimism, Ethereum
Languages
Solidity
Market cap
$4M as of 2026-10-13

About the project

Syndicate builds infrastructure for appchains, application-specific blockchains that projects own and govern themselves. The Syndicate Network lets developers run smart rollups whose sequencing logic sits in on-chain contracts, so transaction ordering, fees and governance can be customised. The SYND token serves as the network gas token and as its governance token.

Findings

Combined across all 7 reviews softstack has completed for Syndicate. Each report is broken out below.

1Critical
6High
18Medium
14Low
8Informational

Reports

Newest first. Every report covers its own scope and commit.

  1. Staking & Emissions V2 smart contract audit

    · commit a533ed9 · 9 files

    PDF
    0Critical
    0High
    1Medium
    3Low
    3Informational

    6 of 7 resolved

    ScopeSource repositorygithub.com/SyndicateProtocol/syndicate-appchains

    Components reviewed

    9 modules · 9 files
    1. src/staking/interfaces/IGasDataProvider.sol
    2. src/staking/EpochTracker.sol
    3. src/staking/GasAggregator.sol
    4. src/staking/GasArchive.sol
    5. src/staking/BlockHashRelayer.sol
    6. src/staking/RewardPoolBase.sol
    7. src/staking/AppchainPool.sol
    8. src/staking/PerformancePool.sol
    9. src/staking/Splitter.sol
    All 7 findings
    IDTitleSeverityStatus
    6.2.1Admin Can Permanently Stall Epoch Advancement

    The removeSequencingChain() function contains a critical logic flaw that permanently prevents epoch advancement when the last sequencing chain is removed. The bug stems from checking the chain count after removal instead of before.

    Mediumfixed
    6.2.2Deterministic getNextChainId() enables frontrunning to brick auto-increment namespace

    The SyndicateFactory.createSyndicateSequencingChain() function supports both manual chain ID assignment and automatic ID allocation via appchainId = 0.

    Lowacknowledged
    6.2.3ChainSubmitted event emitted after epoch increment

    When the last chain submits, the contract emits EpochCompleted(epoch), then increments epoch, and then emits ChainSubmitted(epoch, seqChainID). This causes ChainSubmitted to reference the next epoch instead of the one just completed.

    Lowfixed
    6.2.4PerformancePool uses global stake for per-appchain rewards

    getClaimableAmount calculates a user’s share using their global stake (getUserStakeShare) divided by the appchain’s total stake (getAppchainStake).

    Lowfixed
    6.2.5Unused imports

    ISyndicateSequencingChain and GasCounter are imported but not used.

    Informationalfixed
    6.2.6Duplicate import of Initializable

    Initializable is imported twice from the same path, increasing bytecode size and reducing readability..

    Informationalfixed
    6.2.7Unused import in Splitter

    Address is imported but not used.

    Informationalfixed
  2. Token Staking & Emissions smart contract audit

    · commit ec648b2

    PDF
    0Critical
    1High
    4Medium
    2Low
    0Informational

    4 of 7 resolved

    ScopeSource repositorygithub.com/SyndicateProtocol/syndicate-appchains

    Components reviewed

    1 module
    1. See the published report for the file list
    All 7 findings
    IDTitleSeverityStatus
    6.2.1No Destination Zero Check in claimAllRewards

    The `SyndStaking.claimAllRewards` iterates over provided pools and calls `IPool(pool).claimFor(epochIndex, user, destination)` with the caller-supplied `destination`, but performs no zero-address check.

    Highfixed
    6.2.2Unnecessary Payable Modifier in stageStakeTransfer

    The function performs purely accounting updates for moving stake between appchains. Despite being `payable`, it neither reads nor uses `msg.value`, and no ETH is expected during a stake transfer.

    Mediumfixed
    6.2.3Function Selector Tight Coupling in L1Relayer Cross- Chain Messaging

    The L1Relayer contract's `_relay` function uses `this.relay.selector` when encoding the message payload for the L2Relayer.

    Mediumfixed
    6.2.4Epoch Underflow Before Start Time in EpochTracker

    The `EpochTracker` contract calculates the current epoch as: return ((block.timestamp - START_TIMESTAMP) / EPOCH_DURATION) + 1; If `block.timestamp < START_TIMESTAMP`, this subtraction underflows and reverts in Solidity ≥0.8.

    Mediumacknowledged
    6.2.5Double-spend/Incorrect Fee Accounting in L2Relayer

    The `_relay` function in L2Relayer sets `tokenTotalFeeAmount = amount` and computes `l2CallValue = amount - gasCost`.

    Mediumacknowledged
    6.2.6Division Precision Loss in Reward Calculation

    In `BasePool.getClaimableAmount()` at line 137, the reward calculation uses integer division: uint256 userRewardShare = (rewardTotal * userStakedAmount) / totalStakedAmount; This calculation truncates any remainder from the division…

    Lowfixed
    6.2.7Gas Price Manipulation in GasCounter

    The `GasCounter._trackGas()` function calculates gas cost as `gasUsed * tx.gasprice` (line 62 in GasCounter.sol).

    Lowacknowledged
  3. Core Protocol smart contract audit

    · commit e736a08

    PDF
    0Critical
    1High
    3Medium
    1Low
    0Informational

    5 of 5 resolved

    ScopeSource repositorygithub.com/SyndicateProtocol/syndicate-appchains

    Components reviewed

    1 module
    1. See the published report for the file list
    All 5 findings
    IDTitleSeverityStatus
    6.2.1Unbounded Array Growth Leading to Denial-of- Service in TeeModule

    The submitAssertion() function in TeeModule.sol allows new assertions to be added to the pendingAssertions array. However, the contract fails to enforce a size limit on this array.

    Highfixed
    6.2.2Unbounded Gas Consumption DoS in Multiple Locations

    Several key functions throughout the contract system accept arrays as input or iterate through lists without enforcing any size limits. This includes bulk transaction processing, permission checking loops, and atomic sequencing operations.

    Mediumfixed
    6.2.3Chain ID Collision Vulnerability in getNextChainId()

    The getNextChainId() function generates new chain IDs by converting a namespacePrefix and the nextAutoChainId to strings and concatenating them. This method is unsafe as it creates ambiguous results.

    Mediumfixed
    6.2.4Missing Implementation Validation in upgradeImplementation()

    The upgradeImplementation() function is responsible for upgrading the logic contract for all beacon proxies.

    Mediumfixed
    6.2.5Front-Running Attack on AssertionPoster Configuration

    The configure() function in AssertionPoster.sol is intended to be called via delegatecall from an executor contract. The function correctly validates that address(this) == address(executor), ensuring it runs in the context of the executor.

    Lowfixed
  4. Crosschain Token (SYND) smart contract audit

    · commit 27a4a95

    PDF
    0Critical
    3High
    3Medium
    2Low
    1Informational

    8 of 9 resolved

    ScopeSource repositorygithub.com/SyndicateProtocol/syndicate-appchains

    Components reviewed

    1 module
    1. See the published report for the file list
    All 9 findings
    IDTitleSeverityStatus
    6.2.1Unlimited Token Minting via Crosschain Bridge

    The crosschainMint function within the SyndicateTokenCrosschain.sol contract is missing a critical validation step: it fails to check minting requests against the global TOTAL_SUPPLY of 1 billion tokens.

    Highfixed
    6.2.2Bridge Manager Privilege Escalation

    The protocol's access control model contains a critical flaw related to the BRIDGE_MANAGER_ROLE. This role is overly permissive, allowing a manager to add any address as an authorized bridge without requiring secondary approval or…

    Highfixed
    6.2.3Emission Schedule Bypass

    The crosschain bridge system operates entirely independently of the SyndicateTokenEmissionScheduler.

    Highfixed
    6.2.4Predictable Rate Limiting Allows Burst Minting

    The rate-limiting mechanism for the crosschain bridge is implemented with a fixed 24-hour reset cycle. This design, while functional, is predictable.

    Mediumfixed
    6.2.5Gas Griefing via Bridge Removal Function

    The removeBridge function is implemented with an inefficient design that iterates through the entire array of registered bridges to find and remove a specific entry.

    Mediumfixed
    6.2.6Transfer Lock Period Concerns

    While the base SyndicateToken contract correctly prevents most transfers when a lock is active, the crosschainMint function does not respect this state.

    Mediumfixed
    6.2.7Bridge Limit Bypass via Reconfiguration

    This flaw enables the daily rate limit for a bridge to be improperly reset.. When a BRIDGE_MANAGER_ROLE holder calls this function to reconfigure a bridge's limits, the function completely resets the currentMintUsed counter to zero.

    Lowacknowledged
    6.2.8Missing Input Validation on Bridge Addresses

    The setBridgeLimits function lacks comprehensive input validation for the bridge address parameter. While it correctly checks for a zero address, it fails to verify that the address being added is a smart contract.

    Lowfixed
    6.2.9Divergence from Documentation: Bridge Config & Factory Removal

    We observed two points where the implementation diverged slightly from the documentation. In both cases, these represent enhancements to the system's flexibility and were accurately explained. 1.

    Informationalfixed
  5. Smart Wallets smart contract audit

    · commit 555b91b

    PDF
    1Critical
    1High
    1Medium
    0Low
    0Informational

    3 of 3 resolved

    ScopeSource repositorygithub.com/SyndicateProtocol/protocol-v2

    Components reviewed

    1 module
    1. See the published report for the file list
    All 3 findings
    IDTitleSeverityStatus
    6.2.1Unusable Smart Wallets Created By Factory

    The SmartWalletFactory is a contract to create smart wallets by creating an ExecOperator, SmartWalletModule and SmartWalletMixinManager.

    Criticalfixed
    6.2.2Unchecked Return Parameter In Validity Check

    The SmartWalletModule has two functions to execute transactions: execTransactionFromModule and execTransactionFromModuleReturnData.

    Highfixed
    6.2.3Invalid Token Receiver Address Due To Wrong Padded Calldata

    The RecurringPaymentMixin checks given calldata for validity. The data is expected to be the hex representation of a token transfer function call, which requires a recipient and a token amount.

    Mediumfixed
  6. Smart Wallets smart contract audit

    PDF

    Earlier issue of the same review. Its findings are counted once in the combined total above.

    1Critical
    1High
    1Medium
    0Low
    0Informational

    0 of 3 resolved

    All 3 findings
    IDTitleSeverityStatus
    6.2.1Unusable Smart Wallets Created By Factory

    The SmartWalletFactory is a contract to create smart wallets by creating an ExecOperator, SmartWalletModule and SmartWalletMixinManager.

    Criticalwont-fix
    6.2.2Unchecked Return Parameter In Validity Check

    The SmartWalletModule has two functions to execute transactions: execTransactionFromModule and execTransactionFromModuleReturnData.

    Highwont-fix
    6.2.3Invalid Token Receiver Address Due To Wrong Padded Calldata

    The RecurringPaymentMixin checks given calldata for validity. The data is expected to be the hex representation of a token transfer function call, which requires a recipient and a token amount.

    Mediumwont-fix
  7. ERC721M smart contract audit

    · commit a4fc81e · 1 file

    PDF
    0Critical
    0High
    4Medium
    3Low
    3Informational
    Scope

    Components reviewed

    1 module · 1 file
    1. ERC721MWithCustomTokenId.sol
    All 9 findings
    IDTitleSeverityStatus
    6.2.1Unsafe Minting Function

    The minting functions are utilizing the ERC721 _mint function. The _mint function does not check, if the token receiver can receive ERC721 tokens, which may result in the loss of funds.

    Mediumacknowledged
    6.2.2Unsafe Transfer Function

    The controllerTransfer function utilizes the ERC721 _transfer function. The _transfer function does not check, if the token receiver is capable of receiving ERC721 tokens, which may result in the loss of funds.

    Mediumacknowledged
    6.2.3Missing safeMint Function

    The ERC721UpgradableFork contract is a fork of OpenZeppelins ERC721Upgradeable but is missing the _safeMint function. The current implementation implements the _mint function, which is missing some safety checks on minting new tokens.

    Mediumacknowledged
    6.2.4Overpowered Admin Rights

    This token contracts are holding admin functions to transfer and redeem any ERC721 token of that specific contract.

    Mediumfixed
    6.2.5Improper Input Validation

    The create function accepts setupContracts and data arrays for initializing calls on these contracts but does not ensure that their lengths are the same, which may result in undesired behaviour.

    Lowfixed
    6.2.6Improper Input Validation

    The bulkMintToNAddresses function accepts accounts and quantities arrays for minting different amounts of ERC721MA tokens, to different addresses but does not ensure that their lengths are the same, which may result in undesired behaviour.

    Lowfixed
    6.2.7Wrong Function Modifier

    The create function is marked as payable but it does not handle any incoming funds, which may lead to permanently locking funds.

    Lowfixed
    6.2.8Repeated Existence Check

    In the _isApprovedOrOwner function, the _exists(tokenId) check is being performed twice. Once directly inside _isApprovedOrOwner and again within the ownerOf(tokenId).

    Informationalfixed
    6.2.10Duplicated Paused Check

    The create function of the factory is only callable if the contract is not paused by being locked with the whenNotPaused modifier.

    Informationalfixed
  8. ERC721M smart contract audit

    · 1 file

    PDF

    Earlier issue of the same review. Its findings are counted once in the combined total above.

    0Critical
    1High
    3Medium
    3Low
    3Informational

    0 of 10 resolved

    Scope

    Components reviewed

    1 module · 1 file
    1. ERC721MWithCustomTokenId.sol
    All 10 findings
    IDTitleSeverityStatus
    6.2.1Overpowered Admin Rights

    This token contracts are holding admin functions to transfer and redeem any ERC721 token of that specific contract.

    Highwont-fix
    6.2.2Unsafe Minting Function

    The minting functions are utilizing the ERC721 _mint function. The _mint function does not check, if the token receiver can receive ERC721 tokens, which may result in the loss of funds.

    Mediumwont-fix
    6.2.3Unsafe Transfer Function

    The controllerTransfer function utilizes the ERC721 _transfer function. The _transfer function does not check, if the token receiver is capable of receiving ERC721 tokens, which may result in the loss of funds.

    Mediumwont-fix
    6.2.4Missing safeMint Function

    The ERC721UpgradableFork contract is a fork of OpenZeppelins ERC721Upgradeable but is missing the _safeMint function. The current implementation implements the _mint function, which is missing some safety checks on minting new tokens.

    Mediumwont-fix
    6.2.5Improper Input Validation

    The create function accepts setupContracts and data arrays for initializing calls on these contracts but does not ensure that their lengths are the same, which may result in undesired behaviour.

    Lowwont-fix
    6.2.6Improper Input Validation

    The bulkMintToNAddresses function accepts accounts and quantities arrays for minting different amounts of ERC721MA tokens, to different addresses but does not ensure that their lengths are the same, which may result in undesired behaviour.

    Lowwont-fix
    6.2.7Wrong Function Modifier

    The create function is marked as payable but it does not handle any incoming funds, which may lead to permanently locking funds.

    Lowwont-fix
    6.2.8Repeated Existence Check

    In the _isApprovedOrOwner function, the _exists(tokenId) check is being performed twice. Once directly inside _isApprovedOrOwner and again within the ownerOf(tokenId).

    Informationalwont-fix
    6.2.9Duplicated Functions in Contract

    The functions mintTo and bulkMintToOneAddress at line numbers 216 & 235 appear to perform the exact same function, leading to redundancy.

    Informationalwont-fix
    6.2.10Duplicated Paused Check

    The create function of the factory is only callable if the contract is not paused by being locked with the whenNotPaused modifier.

    Informationalwont-fix
  9. Protocol v2 smart contract audit

    · commit 02e1309 · 30 files

    PDF
    0Critical
    0High
    2Medium
    3Low
    1Informational

    2 of 6 resolved

    ScopeSource repositorygithub.com/SyndicateProtocol/protocol-v2

    Components reviewed

    14 modules · 30 files
    1. src/contracts/guards/common/GuardMixinManager.sol
    2. src/contracts/guards/mixins/LockableSettings.sol
    3. src/contracts/guards/mixins/TokenGated.sol
    4. src/contracts/modules/utils/Constants.sol
    5. src/contracts/modules/utils/IFeeManager.sol
    6. src/contracts/modules/utils/FeeManager.sol
    7. src/contracts/modules/ERC20Club/mint/DepositExchangeTokenMintModule.sol
    8. MintModule.solc5665624a1276853317e8bd698c4901asrc/contracts/modules/ERC20Club/mint/EthMintModule.sol
    9. Fork.sol
    10. src/contracts/ERC721Collective/ERC721Collective.sol
    11. src/contracts/common/ITokenEnforceable.sol
    12. src/contracts/common/TokenRecoverable.sol
    13. src/contracts/common/IERC1644.sol
    14. src/contracts/utils/TokenOwnerChecker.sol
    All 6 findings
    IDTitleSeverityStatus
    6.2.1Overpowered Owner Rights

    The ERC20Club token holds functions, that permits specific addresses to transfer or burn tokens of other users, when the contract is in controllable state.

    Mediumacknowledged
    6.2.2Maximum Member Count

    The isAllowed function is missing a require, as at the moment it’s not allowed for member to send all their tokens to a new member, in the case the member count is at the maximum.

    Mediumfixed
    6.2.3Redundant Overflow Check

    The getMintAmount function checks for an potential overflow before calling a safe multiply operation. Since Solidity version 0.8.0, arithmetical operation are automatically checking for over- and underflows.

    Lowfixed
    6.2.4Unused Function Parameter

    The calculate function takes two parameters: minter and depositAmount. The minter address is never used in the implementation of the function and can be removed.

    Lowacknowledged
    6.2.5Missing Value Verification

    Certain functions lack a value safety check. Therefore, only values that are consistent with the logic of the contract should be permitted.

    Lowacknowledged
    6.2.6Uninitialized Variables

    Certain functions lack of explicit variable initialization. Implicitly, uint variables are set to zero.

    Informationalacknowledged

Methodology

Review process

  1. Scoping and threat modelling

    The commit hash is frozen and the attack surface is mapped: asset flows, trust assumptions, privileged roles and the external systems the code depends on.

  2. Automated analysis

    Static analysers sweep the full codebase to surface known weakness patterns, unreachable states and deviations from established conventions.

  3. Manual code review

    Independent engineers read the code line by line, concentrating on business logic, access control, arithmetic and the handling of external calls.

  4. Adversarial testing

    Property-based fuzzing and invariant tests attack the assumptions the protocol relies on, targeting states that ordinary unit tests never reach.

  5. Reporting

    Every finding is documented with a severity rating, its impact, the steps to reproduce it and a concrete remediation the team can act on.

  6. Remediation verification

    Submitted fixes are re-reviewed against the original finding, and the final report records the resolved state of each one.

Standards and references

  • SWC RegistrySmart Contract Weakness Classification
  • CWECommon Weakness Enumeration

FAQ

7 separate reviews, the first in August 2022 and the most recent in December 2025. Each one covers its own scope and commit, and is listed individually on this page.

47 findings across all 7 reviews: 1 critical, 6 high, 18 medium, 14 low and 8 informational.

Each report covers the code as it stood at the commit it names, the most recent in December 2025. Any code changed after that point is outside the reviewed scope.