Platform: Code4rena
Start Date: 22/03/2024
Pot Size: $36,500 USDC
Total HM: 7
Participants: 17
Period: 14 days
Judge: Lambda
Id: 323
League: POLKADOT
Rank: 16/17
Findings: 1
Award: $98.99
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: DarkTower
Also found by: 0xepley, Bauchibred, LinKenji, ZanyBonzy, hunter_w3b
98.9907 USDC - $98.99
The core mechanisms include
The key components covered in our analysis are the aUSD CDP mechanism, LDOT/LCDOT liquid staking, and the new Collator Pool design for decentralized collator nomination.
The core Acala liquidity system consists of several key pallets:
The pallets interact closely to enable the CDP and staking derivative functionality.
graph TD Honzon --> |Mint/Burn aUSD| Cdp-Treasury Honzon --> |Create/Close CDPs| Collateral-Tokens Collateral-Tokens --> |Deposit/Withdraw| Cdp-Treasury Cdp-Treasury --> |Stability Fees| Incentives LDOTStaking --> |Mint/Burn LDOT| DOT-Tokens Homa --> |Mint/Burn xDOT| DOT-Tokens Homa --> |Distribute Rewards| Incentives DEX --> |Trading Fees| Incentives Collator-Selection --> |Bond/Unbond| DOT-Tokens
The Acala code follows Rust best practices and uses common FRAME/Substrate idioms. Some notable observations:
decl_module
, decl_storage
, decl_event
macrosunsafe
code and uses well-audited libraries like sp-runtime
However, there are some areas for improvement:
Here are a few representative examples of the code style and abstractions used:
// Simple Config trait used for currency-related constants pub trait AUSDConfig { fn min_price_change() -> Ratio; fn max_liquidation_ratio() -> Ratio; fn liquidation_penalty() -> Rate; // ... } // Struct representing a CDP #[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode)] pub struct CDP<AccountId, Balance> { pub collateral_type: CurrencyId, pub owner: AccountId, pub collateral: Balance, pub issue: Balance, } // Event emitted when a new CDP is created fn deposit_event() = default; fn deposit_event_indexed(topics: Vec<T::Hash>) { Self::deposit_event(RawEvent::NewCDP(topics[0], topics[1], topics[2])); }
The code makes heavy use of generic types, traits, and encoded structs to allow for flexibility in the specific currency and account types used.
Oracle attacks and manipulation
Homa unbonding griefing
Collator pool centralization
Complexity and cross-chain attack surface
Liquidity-based liquidation spirals
Dependency on Polkadot security and liveness
While the Acala code is well-structured and follows best practices, there are a number of potential vulnerabilities, edge cases, and centralization risks that warrant further investigation and mitigation. Many of these risks result from the interaction of multiple complex pallets and user roles.
The risk of economic attacks, oracle manipulation, and cascading liquidations also needs to be carefully modeled and stress-tested. Acala should prioritize progressive decentralization of privileged admin roles and explore approaches like time-delays or multi-sig to limit potential centralized abuse.
Recommendations:
Acala is a powerful and ambitious liquidity protocol with the potential to unlock significant DOT liquidity. However, the complexity of the system and reliance on external actors creates substantial risks that need to be carefully managed.
With further refinement, progressive decentralization, and proactive risk management, Acala can evolve into a battle-tested pillar of the Polkadot DeFi ecosystem. Let me know if you have any other questions!
More in-depth analysis of potential security vulnerabilities and improvement points in the provided code: *******************************8
a. The accumulate_incentives
function iterates over all pools and calls transfer_rewards_and_update_records
for each pool. If there are many pools, this could potentially lead to high gas consumption and potentially hit the block gas limit. Consider batching the reward accumulation process or optimizing it to reduce the number of iterations.
b. The [transfer_rewards_and_update_records
](https://github.com/code-423n4/2024-03-acala/blob/9c71c05cf2d9f0a2603984c50f76fc8a315d4d65/src/modules/incentives/src/lib.rs#L389-L401) function uses map_err
to handle errors during the reward transfer and update process. However, it only logs the error and doesn't propagate it further. Consider handling errors more explicitly and deciding whether to revert the transaction or take alternative actions based on the error type.
c. The claim_rewards
function calls do_claim_rewards
, which iterates over all pending rewards for a user. If a user has a large number of pending rewards, this could potentially lead to high gas consumption. Consider implementing pagination or limiting the number of rewards that can be claimed in a single transaction.
d. The module relies on the RewardsSource
account to fund the incentive rewards. Ensure that this account is properly secured and has sufficient funds to cover the expected incentive payouts. Implement proper access controls and monitoring for this account.
a. The add_share
and remove_share
functions update the reward records for each currency in the pool. If there are many reward currencies in a pool, this could potentially lead to high gas consumption. Consider optimizing the reward record updates to minimize the number of storage writes.
b. The claim_one
function calculates the claimable reward amount using U256
arithmetic operations. While U256
helps prevent overflows, it's important to ensure that the arithmetic operations are performed correctly and don't result in unintended rounding or precision issues.
c. The remove_share
function calls claim_rewards
before updating the user's share balance. This ensures that the user's pending rewards are claimed before their share is reduced. However, this also means that the user's share balance is not updated atomically with the reward claim. Consider whether this behavior aligns with the intended design and if there are any potential race conditions or exploits that could arise from this non-atomic update.
a. The unbond
and unbond_instant
functions allow users to unbond their tokens. Ensure that the unbonding process is properly validated and that users cannot unbond more tokens than they have bonded. Implement proper checks to prevent any potential overflows or underflows in the unbonding calculations.
b. The withdraw_unbonded
function allows users to withdraw their unbonded tokens after the unbonding period has passed. Ensure that the unbonding period is enforced correctly and that users cannot withdraw their tokens before the specified period has elapsed. Consider implementing additional checks or time-locks to prevent premature withdrawals.
Inaccurate share calculation and inflation in add_share()
add_share()
has complex share inflation logicU256 mul/div
to calculate share inflation, but this could potentially be exploited to mint excess shareschecked_mul/checked_div
Exploiting instant unbond deduction fee
unbond_instant()
lets users unbond by paying a fee defined by InstantUnstakeFee
checked_mul()
or saturating_mul()
for fee calculationInconsistent reward deduction and rounding behavior
ClaimRewardDeductionRates
when users claim rewardsCentralization Risks:
Privileged UpdateOrigin can arbitrarily change pool incentives
update_incentive_rewards()
allows UpdateOrigin to modify incentive rewards for any poolRewardsSource is a centralized reward funding account
Unbonding and rebonding grief attack
unbond()
and rebond()
Incentive reward front-running
update_incentive_rewards()
Manipulating DexShare to maximize rewards
Reliance on PolkadotJs and ecosystem libraries
Staking derivatives and systemic risk
Oracle and price manipulation risk
Architecture Overview:
graph TD subgraph Acala Honzon[Honzon CDPs] LDOTStaking[LDOT Staking] Homa[Homa Liquid Staking] DEX[Built-in DEX AMM] Incentives[Incentives Distribution] Treasury[Central Treasury] CollatorPool[Collator Pool] end Polkadot -- Validation & Security --> Honzon Honzon -- Stability Fees --> Treasury Honzon -- Minting & Burning --> aUSD LDOTStaking --Mints LDOT --> xDOT Homa --Mints xDOT --> xDOT xDOT -- Collateral --> Honzon DEX -- Trading Fees --> Treasury DEX -- LP Incentives --> Incentives Treasury -- Funding --> Incentives CollatorPool -- Bond LCDOT --> xDOT
Admin Flow
graph TD Admin -- Change Collateral Factors --> Honzon Admin -- Set LDOT/xDOT Rates --> LDOTStaking Admin -- Set Liquidation Params --> Honzon Admin -- Add/Remove Collators --> CollatorPool Admin -- Pause Liquidations --> Honzon Admin -- Set AMM Fees --> DEX Admin -- Allocate Incentives --> Incentives
User Flow
graph TD User -- Open CDP --> Honzon User -- Deposit DOT --> Honzon User -- Mint aUSD --> Honzon User -- Stake DOT --> LDOTStaking User -- Mint xDOT --> Homa User -- Provide Liquidity --> DEX User -- Swap Tokens --> DEX User -- Delegate to Collators --> CollatorPool
Core Contract
graph TD Honzon -- Collateral Deposit --> CollateralAssets Honzon -- Borrow aUSD --> aUSD Honzon -- Liquidate CDP --> DEX Honzon -- Stability Fees --> Treasury LDOTStaking -- Stake DOT --> xDOT LDOTStaking -- Claim Rewards --> Incentives Homa -- xDOT Minting --> xDOT Homa -- Staking Rewards --> Incentives DEX -- Token Swaps --> TradingPairs DEX -- Add Liquidity --> LiquidityPools DEX -- LP Rewards --> Incentives CollatorPool -- Stake LCDOT --> xDOT
Contract Analysis (with risk highlights):
classDiagram class Honzon { +openCDP() +depositCollateral() +borrowaUSD() +repay() +closeCDP() +liquidate() +stabilityFee() } class LDOTStaking { +stakeXDOT() +stakeLDOT() +claim() } class Homa { +mintHoma() +requestRedeem() +processRequests() +claimRewards() } class DEX { +swapExactTokensForTokens() +addLiquidity() +removeLiquidity() +claimLPRewards() } class CollatorPool { +bond() +nominate() +revoke() +withdrawUnbonded() +claim() } class xDOT { +transfer() +approve() +mint() +redeem() } class aUSD { +transfer() +approve() +mint() +burn() } Honzon --|> xDOT : uses Honzon --|> aUSD : mints/burns LDOTStaking --|> xDOT : mints Homa --|> xDOT : mints DEX --> aUSD : trades DEX --> xDOT : trades CollatorPool --|> xDOT : bonds class AdminRisks { Privileged access Enforce fund segregation Limit role scope/duration } class SystemicRisks { Peg loss Liquidation cascade Cross-chain failures } class OracleRisks { Price manipulation Flash loan attacks Stale or invalid prices } AdminRisks --> Honzon AdminRisks --> CollatorPool SystemicRisks --o aUSD SystemicRisks --o xDOT OracleRisks --> Honzon OracleRisks --> DEX
the Acala protocol has a complex architecture with multiple interacting components including CDPs, liquid staking derivatives, an on-chain DEX, and a collator nomination pool. This complexity introduces several risks:
Admin privileges are highly centralized and could be abused to drain funds or manipulate markets. Progressive decentralization and limiting role scope are recommended.
The interactions between aUSD, xDOT, and other bridged assets create systemic risk. A failure in one component like liquidations or price oracles could ripple across the system, breaking the aUSD peg and depleting liquidity. Extensive stress-testing and circuit breakers are advisable.
The reliance on AMM pricing and centralized price oracles opens attack vectors for price manipulation via flash loans or liquidity attacks. Acala should pursue more robust decentralized oracles and consider cross-chain liquidity backstops.
The collator nomination pool has the potential for stake centralization over time as the highest-yielding collators attract the most delegations. Nomination limits and governance safeguards may help preserve decentralization.
While Acala's architecture is well-structured and modular, the combination of staking derivatives, bridged assets, and cross-chain liquidity introduces substantial complexity and risk. Continued defensive engineering, decentralization of privileged roles, and proactive risk modeling will be essential to maturing Acala into a robust DeFi hub for Polkadot.
6 hours
#0 - c4-pre-sort
2024-04-07T15:24:00Z
DadeKuma marked the issue as sufficient quality report
#1 - DadeKuma
2024-04-07T15:24:09Z
Warden has submitted HM | AI Generated Report | Useful Diagrams / Tables | Insightful Content | Useful Suggestions | Format |
---|---|---|---|---|---|
❌ | 🔻 | ✔️ | ➖ | ➖ | ✔️ |
Notes: There are some good suggestions, while others are not. Hints to real issues in the codebase even if the Warden hasn't submitted them.
#2 - c4-judge
2024-04-09T16:04:29Z
OpenCoreCH marked the issue as grade-b