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.
Reports
Newest first. Every report covers its own scope and commit.
Staking & Emissions V2 smart contract audit
0Critical0High1Medium3Low3Informational6 of 7 resolved
Scope
Source repositorygithub.com/SyndicateProtocol/syndicate-appchainsComponents reviewed
9 modules · 9 files- src/staking/interfaces/IGasDataProvider.sol
- src/staking/EpochTracker.sol
- src/staking/GasAggregator.sol
- src/staking/GasArchive.sol
- src/staking/BlockHashRelayer.sol
- src/staking/RewardPoolBase.sol
- src/staking/AppchainPool.sol
- src/staking/PerformancePool.sol
- src/staking/Splitter.sol
All 7 findings
ID Title Severity Status 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.
Medium fixed 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.
Low acknowledged 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.
Low fixed 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).
Low fixed 6.2.5Unused imports ISyndicateSequencingChain and GasCounter are imported but not used.
Informational fixed 6.2.6Duplicate import of Initializable Initializable is imported twice from the same path, increasing bytecode size and reducing readability..
Informational fixed 6.2.7Unused import in Splitter Address is imported but not used.
Informational fixed Token Staking & Emissions smart contract audit
0Critical1High4Medium2Low0Informational4 of 7 resolved
Scope
Source repositorygithub.com/SyndicateProtocol/syndicate-appchainsComponents reviewed
1 module- See the published report for the file list
All 7 findings
ID Title Severity Status 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.
High fixed 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.
Medium fixed 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.
Medium fixed 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.
Medium acknowledged 6.2.5Double-spend/Incorrect Fee Accounting in L2Relayer The `_relay` function in L2Relayer sets `tokenTotalFeeAmount = amount` and computes `l2CallValue = amount - gasCost`.
Medium acknowledged 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…
Low fixed 6.2.7Gas Price Manipulation in GasCounter The `GasCounter._trackGas()` function calculates gas cost as `gasUsed * tx.gasprice` (line 62 in GasCounter.sol).
Low acknowledged Core Protocol smart contract audit
0Critical1High3Medium1Low0Informational5 of 5 resolved
Scope
Source repositorygithub.com/SyndicateProtocol/syndicate-appchainsComponents reviewed
1 module- See the published report for the file list
All 5 findings
ID Title Severity Status 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.
High fixed 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.
Medium fixed 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.
Medium fixed 6.2.4Missing Implementation Validation in upgradeImplementation() The upgradeImplementation() function is responsible for upgrading the logic contract for all beacon proxies.
Medium fixed 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.
Low fixed Crosschain Token (SYND) smart contract audit
0Critical3High3Medium2Low1Informational8 of 9 resolved
Scope
Source repositorygithub.com/SyndicateProtocol/syndicate-appchainsComponents reviewed
1 module- See the published report for the file list
All 9 findings
ID Title Severity Status 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.
High fixed 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…
High fixed 6.2.3Emission Schedule Bypass The crosschain bridge system operates entirely independently of the SyndicateTokenEmissionScheduler.
High fixed 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.
Medium fixed 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.
Medium fixed 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.
Medium fixed 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.
Low acknowledged 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.
Low fixed 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.
Informational fixed Smart Wallets smart contract audit
1Critical1High1Medium0Low0Informational3 of 3 resolved
Scope
Source repositorygithub.com/SyndicateProtocol/protocol-v2Components reviewed
1 module- See the published report for the file list
All 3 findings
ID Title Severity Status 6.2.1Unusable Smart Wallets Created By Factory The SmartWalletFactory is a contract to create smart wallets by creating an ExecOperator, SmartWalletModule and SmartWalletMixinManager.
Critical fixed 6.2.2Unchecked Return Parameter In Validity Check The SmartWalletModule has two functions to execute transactions: execTransactionFromModule and execTransactionFromModuleReturnData.
High fixed 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.
Medium fixed Smart Wallets smart contract audit
Earlier issue of the same review. Its findings are counted once in the combined total above.
1Critical1High1Medium0Low0Informational0 of 3 resolved
All 3 findings
ID Title Severity Status 6.2.1Unusable Smart Wallets Created By Factory The SmartWalletFactory is a contract to create smart wallets by creating an ExecOperator, SmartWalletModule and SmartWalletMixinManager.
Critical wont-fix 6.2.2Unchecked Return Parameter In Validity Check The SmartWalletModule has two functions to execute transactions: execTransactionFromModule and execTransactionFromModuleReturnData.
High wont-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.
Medium wont-fix ERC721M smart contract audit
0Critical0High4Medium3Low3InformationalScope
Components reviewed
1 module · 1 file- ERC721MWithCustomTokenId.sol
All 9 findings
ID Title Severity Status 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.
Medium acknowledged 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.
Medium acknowledged 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.
Medium acknowledged 6.2.4Overpowered Admin Rights This token contracts are holding admin functions to transfer and redeem any ERC721 token of that specific contract.
Medium fixed 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.
Low fixed 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.
Low fixed 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.
Low fixed 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).
Informational fixed 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.
Informational fixed ERC721M smart contract audit
Earlier issue of the same review. Its findings are counted once in the combined total above.
0Critical1High3Medium3Low3Informational0 of 10 resolved
Scope
Components reviewed
1 module · 1 file- ERC721MWithCustomTokenId.sol
All 10 findings
ID Title Severity Status 6.2.1Overpowered Admin Rights This token contracts are holding admin functions to transfer and redeem any ERC721 token of that specific contract.
High wont-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.
Medium wont-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.
Medium wont-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.
Medium wont-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.
Low wont-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.
Low wont-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.
Low wont-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).
Informational wont-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.
Informational wont-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.
Informational wont-fix Protocol v2 smart contract audit
0Critical0High2Medium3Low1Informational2 of 6 resolved
Scope
Source repositorygithub.com/SyndicateProtocol/protocol-v2Components reviewed
14 modules · 30 files- src/contracts/guards/common/GuardMixinManager.sol
- src/contracts/guards/mixins/LockableSettings.sol
- src/contracts/guards/mixins/TokenGated.sol
- src/contracts/modules/utils/Constants.sol
- src/contracts/modules/utils/IFeeManager.sol
- src/contracts/modules/utils/FeeManager.sol
- src/contracts/modules/ERC20Club/mint/DepositExchangeTokenMintModule.sol
- MintModule.solc5665624a1276853317e8bd698c4901asrc/contracts/modules/ERC20Club/mint/EthMintModule.sol
- Fork.sol
- src/contracts/ERC721Collective/ERC721Collective.sol
- src/contracts/common/ITokenEnforceable.sol
- src/contracts/common/TokenRecoverable.sol
- src/contracts/common/IERC1644.sol
- src/contracts/utils/TokenOwnerChecker.sol
All 6 findings
ID Title Severity Status 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.
Medium acknowledged 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.
Medium fixed 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.
Low fixed 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.
Low acknowledged 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.
Low acknowledged 6.2.6Uninitialized Variables Certain functions lack of explicit variable initialization. Implicitly, uint variables are set to zero.
Informational acknowledged
Methodology
Review process
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.
Automated analysis
Static analysers sweep the full codebase to surface known weakness patterns, unreachable states and deviations from established conventions.
Manual code review
Independent engineers read the code line by line, concentrating on business logic, access control, arithmetic and the handling of external calls.
Adversarial testing
Property-based fuzzing and invariant tests attack the assumptions the protocol relies on, targeting states that ordinary unit tests never reach.
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.
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.