XDAO security record

BNB Chain, Polygon (POS)xdao.app

As of 30 Oct 2026, XDAO has 1 audit report on Trustblock, by Quantstamp, dated 4 Aug 2025. That report lists no open critical findings.

Critical findings
No open critical findings
In 1 report.
Open the latest report
Your label, live
Get the label for your site

Record last updated . It holds audits and findings, contracts and incidents up to Oct 2026. Trustblock does not review code.

Audits1

Newest first

AuditSecurity firmDateScopeCritical findingsReport
XDAOSecurity firm: QuantstampDate: Scope: Not recorded (see the report)No open critical findingsOpen · certificate.quantstamp.com

Findings14

Statuses are the security firm’s own, from each report.

SeverityOpenAcknowledgedFixed
Critical000
High004
Medium008
Low002
HighDAO Actions Fail Due to Insufficient Funds for `make_action()` ExecutionXDAO · Quantstamp · 4 Aug 2025Fixed

When a proposal closes successfully, `elections_master.fc` forwards its entire balance—an amount that equals the exact `service_fee` collectively paid by voters—to `master.fc`. Inside the transferred message body, the contract also encodes that same `service_fee` value. Upon receipt, `master.fc` subtracts both the embedded `fee_value` and the constant `BASE_FEE` from the incoming `msg_value` in order to create the forwarding message that executes the chosen action. In the ordinary path, where no actor contributes extra value, this arithmetic becomes negative (for example, `1 TON – 1 TON – 0.05 TON = -0.05 TON`). FunC then raises a cell-underflow (runtime error 9), the transaction reverts, and execution halts. Because governance automation never appends additional coins, the failure is deterministic: every approved action collapses at the first hop unless the last voter that triggers the execution manually “tops up” the trigger transaction so that `msg_value` is at least `service_fee + BASE_FEE`. The flaw therefore turns the DAO into a voting facade; decisions appear to pass, yet no state change can occur under normal use. The problem is aggravated by the bounce handler in `elections_master.fc`. When `master.fc` reverts, the bounce message arrives carrying the full contract balance, and the handler refunds that amount to `data::initiated_by_address`, the proposal creator. Thus, the collective voting pot is silently funneled to a single participant while all other voters lose their gas fees. The combined effect is a denial-of-service on governance, an economic distortion that rewards initiators for failed actions, and a breach of user trust. At its root, the design double-spends `service_fee`: first as a payment to the fee manager and then implicitly as gas for message forwarding. Without an external subsidy, the ledger can never satisfy both obligations, guaranteeing perpetual under-funding and reversion.

Open the report
HighGovernance Takeover via Unrestricted `elections_master` DeploymentXDAO · Quantstamp · 4 Aug 2025Fixed

The `make_action()` function in the `master` contract recalculates the expected `elections_master` address from input parameters but does not verify that the contract was deployed through the legitimate `create_elections()` flow. An attacker can independently deploy a malicious `elections_master` with a low vote threshold, satisfy it, and invoke `make_action()`, bypassing DAO governance controls and executing arbitrary actions (including contract upgrades).

Open the report
HighIncorrect Storage Overwrite in `recv_external()` Corrupts Plugin StateXDAO · Quantstamp · 4 Aug 2025Fixed

In `walletV4_plugin.recv_external()`, two calls to `set_data()` rebuild the contract state but omit the `is_initialized` flag and conditional `master_dao_address`, causing misaligned storage. Subsequent parsing (e.g., in get-methods like `seqno()`, `get_subwallet_id()`) will read incorrect offsets, leading to parse errors or unintended behaviors.

Open the report
HighMissing Sender Validation in `op::burn_notification` Allows Unauthorized Supply ManipulationXDAO · Quantstamp · 4 Aug 2025Fixed

In `master.recv_internal()`, the `op::burn_notification` branch updates `data::total_supply` without verifying that `sender_address` matches `data::jetton_master_address`. An attacker can send a spoofed `burn_notification` to arbitrarily change the total supply, bypassing the intended governance or burn mechanisms.

Open the report
MediumAbrupt Fee Jump in `_calculate_service_fee()` Leads to Unpredictable CostsXDAO · Quantstamp · 4 Aug 2025Fixed

The `_calculate_service_fee()` function in `factory` determines deployment fees based on `data::count`. Its conditional thresholds cause a sharp and counterintuitive fee drop from over 1200 TON at `count = 500_000` to 25 TON at `count = 500_001`, likely due to a misconfigured threshold. Such unpredictable jumps can confuse users and be exploited by timing contract creations around the boundary. After consulting with the team, we get the expected logic to be a multi-tiered, linear progressive fee: * 1st DAO: 0.1 TON. * 1st to 1,000th DAO: A linear increase from 0.1 TON to 2.5 TON. * 1,001st to 500,000th DAO: A linear increase from 2.5 TON to 25 TON. * 500,001st DAO onwards: A fixed fee of 25 TON.

Open the report
MediumImproper Access Control in `recv_external()` Enables Key Holder Actions After DAO InitializationXDAO · Quantstamp · 4 Aug 2025Fixed

In `walletV4_plugin.recv_external()`, the contract uses `throw_unless(401, is_initialized?)` to gate external messages based on the `is_initialized` flag. This logic is inverted: once `master_dao_address` is set, external messages from the original key holder should be rejected, but the current check allows them. An initialized contract should only accept calls from the DAO, not the key holder, violating the intended security model.

Open the report
MediumImproper Bounce Handling in `elections_master` May Misallocate FundsXDAO · Quantstamp · 4 Aug 2025Fixed

The `on_bounce()` handler in `elections_master` treats any bounced message other than `op::vote_internal` as a failure of the governing action and refunds the entire contract balance to `data::initiated_by_address`. This includes bounces from the `op::make_action` message sent to `master`. If that message fails (e.g., due to gas exhaustion, malformed payloads, or upgrade incompatibilities) the refund goes to the proposal creator rather than preserving funds for voters or protocol maintenance, violating expected fund flows. At the root of the issue is the lack of a clear mechanism to refund the locked fees for a failed proposal. Another failure path occurs when a proposal fails to reach sufficient votes. Both cases should be handled in a consistent manner.

Open the report
MediumInadequate Fee Provision for Multi-Wallet Minting in `op::create_master` Risks Partial FailuresXDAO · Quantstamp · 4 Aug 2025Fixed

In the `factory` contract, the `op::create_master` operation fails to allocate sufficient gas for minting tokens when a new DAO is created with multiple initial members. It sends each mint request to the `jetton_minter` with a `msg_value` of only `BASE_FEE`, which is inadequate to cover the necessary forwarding fees for token distribution. As a result, the `jetton_minter`'s balance is quickly depleted after processing only a few requests. Subsequent minting operations fail silently without alerting the `factory`, which proceeds with the DAO creation as if all tokens were successfully distributed. This leads to an inconsistent state where the DAO is active, but the token allocation is incomplete and the total supply is miscalculated. In detail: 1. **Initial Underfunding**: When `op::create_master` is executed, the `factory` contract deploys a new `jetton_minter` instance, funding it with a minimal balance of `BASE_FEE`. 2. **Inadequate Minting Fee**: For each initial token holder, the `factory` sends an `op::mint` message to the `jetton_minter`. Each of these messages carries a `msg_value` of `BASE_FEE`. 3. **Fee-Draining Forwarding**: Inside the `jetton_minter`, the `op::mint` handler calls the `mint_tokens` function, which is responsible for sending the newly minted tokens to the jetton wallet. This is done via `send_raw_message` using `mode 1`. This mode requires the sender (the `jetton_minter`) to pay for the message forwarding fees from its own balance. 4. **Silent Failures**: The `BASE_FEE` provided with the `op::mint` call only covers the immediate gas costs of that transaction and does not account for the additional forwarding fees. The `jetton_minter` is forced to pay these fees from its own small balance. After a few transactions, this balance is exhausted. 5. **No Error Propagation**: Once the `jetton_minter`'s balance is depleted, subsequent calls to `send_raw_message` fail. Because `mode 1` is used, the failed transaction does not bounce, and no error is propagated back to the `factory`. The `factory` remains unaware of the failure and continues its execution, leading to a partially failed token distribution.

Open the report
MediumMalformed `op::get_fees_notification` Payload in `fee_manager` Breaks Fee UpdatesXDAO · Quantstamp · 4 Aug 2025Fixed

The `recv_internal()` handler for `op::get_fees` in `fee_manager` constructs the `op::get_fees_notification` payload with extra fields (`.store_slice(data::admin_address)` and `.store_slice(in_msg_body)`) right after the fee dictionary, deviating from the expected format used by `factory`, which expects a `query_id`. As a result, the `factory` contract cannot parse the response correctly, leading to failed fee updates or bounced messages, which can disable contract operations that depend on up-to-date fee data.

Open the report
MediumMissing Guard in `vote_confirm_notification()` Allows Multiple Executions of Proposal ActionXDAO · Quantstamp · 4 Aug 2025Fixed

The `vote_confirm_notification()` function in `elections_master.fc` is responsible for tallying votes and executing a proposal's action when the vote threshold is met. The function increments `data::current_amount` and, if it exceeds `data::success_amount`, it sets `data::is_executed` to `true` and sends a `make_action` message to the master DAO contract. However, the function lacks an initial check to determine if the proposal has already been executed. If multiple `vote_confirm_notification` messages are processed after the success threshold has been met, the condition `data::current_amount >= data::success_amount` will remain true for each incoming message. Because there is no guard to check the `data::is_executed` flag at the beginning of the function, each of these subsequent messages will also trigger the `make_action` message, leading to the same DAO action being executed multiple times. This could cause unintended consequences, such as minting tokens multiple times, changing DAO parameters repeatedly, or sending funds more than once.

Open the report
Show 4 more
MediumMissing Opcode Filter for `0xF` in `recv_internal()` Allows Unintended Access to Self-DestructXDAO · Quantstamp · 4 Aug 2025Fixed

The `recv_internal()` function in `walletV4_plugin` initially filters messages by checking if `op` is not one of `0x706c7567`, `0x64737472`, or `0`. However, the self-destruct logic is triggered on `op == 0xF`, which is not included in the filter. As a result, a crafted internal message with `op == 0xF` will get into the `if (op != 0x706c7567) & (op != 0x64737472) & (op != 0) {}` block and returns early without doing the intended action.

Open the report
MediumStale Governance Parameters in `make_action()` Allow Outdated ProposalsXDAO · Quantstamp · 4 Aug 2025Fixed

When a new election is initiated, the `elections_master.fc` contract creates a snapshot of critical governance parameters from the main `master.fc` contract. These cached parameters include the `success_amount` (required votes), the `service_fee` for the proposed action, and the code of the contracts involved (`elections_master_code`, `elections_wallet_code`, etc.). A vulnerability exists because if these core parameters are updated in `master.fc` while an election is still active, the ongoing election will continue to operate using the old, stale data. The `make_action()` function in `master.fc`, which executes the final proposal, does not sufficiently validate all of these cached parameters against its current state. Specifically, the following parameters can become stale: 1. `success_amount`: Can change if the `total_supply` is altered (via `op::call_jetton_mint` or `op::burn_notification`) or if the `success_percentage` is modified (via `op::change_success_percentage`). 2. `service_fee`: Can change if the `master.fc` contract receives a fee update from the `fee_manager` (via `op::get_fees_notification`). 3. Contract code: Can change if a proposal to upgrade the DAO's contracts is passed (via `op::upgrade_code`). While the current implementation correctly invalidates proposals from old contract code by recalculating the sender's address, it fails to perform similar checks for `success_amount` and `service_fee`.

Open the report
LowMismatched `msg_value` Validation and Refund Logic in `op::create_master` Causes Underfunded TransactionsXDAO · Quantstamp · 4 Aug 2025Fixed

In the `factory`’s `op::create_master`, the initial check requires `msg_value > service_fee + BASE_FEE * (6 + mint_messages_count)`, but the refund calculation deducts `service_fee + BASE_FEE * (8 + mint_messages_count)`. This discrepancy allows transactions that pass validation to fail later or refund less than expected, leading to user confusion and potential loss of funds.

Open the report
LowMissing Validation for Zero `success_percentage` in DAO CreationXDAO · Quantstamp · 4 Aug 2025Fixed

In both the `factory`’s `op::create_master` and the `master`’s `op::change_success_percentage`, the code checks that `success_percentage` does not exceed 100%, but it does not enforce a minimum above zero. Generally, we should not expect a proposal to pass with zero votes.

Open the report

Contracts

No deployed address is recorded for these audits.

The reports name a repository or files. Open a report to see its scope.

Show this on your site

Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.

Content

Facts

Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.

Fixed as
Language

Style

Layout
Theme
Accent
Corners

Format

Size
Date
Embed as
HTML
<head>
<script src="https://widget.trustblock.run/project-widget.min.js" defer></script>
</head>
<body>
<div
data-tb-project-widget
data-tb-project-slug="xdao"
></div>
</body>

Paste the code where the label should appear. It uses no API credit and updates by itself.

Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.

Tell us