報告清單
| 項目 | 描述 |
|---|---|
| 客戶 | Magpie XYZ |
| 目標 | CakePie 合約 |
版本歷史
| 版本 | 日期 | 描述 |
|---|---|---|
| 1.0 | 2023 年 11 月 30 日 | 初次發布 |
1. 簡介
1.1 關於目標合約
| 資訊 | 描述 |
|---|---|
| 類型 | 智能合約 |
| 語言 | Solidity |
| 方法 | 半自動化與人工驗證 |
本次審計的目標是 Magpie XYZ 的 CakePie 合約^1程式碼儲存庫。CakePie 合約運行一項 CakeRush 活動,用戶可以在 CakePie 上將其 CAKE 代幣或鎖倉的 CAKE 部位轉換。請注意,僅 CakeRush.sol 和 PancakeStakingBNBChain.sol 包含在審計範圍內,其他檔案則不在本次審計範圍之內。
審計過程是迭代的。具體而言,我們將審計修復已發現問題的提交內容。如果出現新問題,我們將繼續此過程。審計期間的提交 SHA 值如下表所示。我們的審計報告對初始版本(版本 1)的程式碼,以及後續版本中修復報告內問題的新程式碼負責。

1.2 安全模型
為了評估風險,我們遵循產業界和學術界廣泛採用的標準或建議,包括 OWASP 風險評級方法論 ^2 和通用弱點枚舉 ^3。風險的整體嚴重程度由可能性和影響決定。具體而言,可能性用於估計攻擊者發現並利用特定漏洞的可能性,而影響用於衡量成功利用後的後果。
在本報告中,可能性和影響均分為兩個等級,即高和低,其組合如表 1.1 所示。

因此,本報告中的嚴重程度分為三類:高 (High)、中 (Medium)、低 (Low)。為了完整起見,未定 (Undetermined) 也用於涵蓋無法明確界定風險的情況。
此外,已發現項目的狀態將屬於以下四類之一:
-
未定 (Undetermined):尚未收到回應。
-
已獲悉 (Acknowledged):客戶已收到該項目,但尚未確認。
-
已確認 (Confirmed):客戶已認可該項目,但尚未修復。
-
已修復 (Fixed):客戶已確認並修復該項目。
2. 審計發現
總體而言,我們發現了 兩個 潛在問題。此外,我們還提出了 三個 建議和 一個 備註。
-
高風險:1
-
低風險:1
-
建議:3
-
備註:1
| ID | 嚴重程度 | 描述 | 類別 | 狀態 |
|---|---|---|---|---|
| 1 | 低 | 參數重置後潛在的狀態不一致 | 軟體安全 | 已修復 |
| 2 | 高 | 重複領取 mCake 獎勵 | 軟體安全 | 已修復 |
| 3 | - | 檢查初始化函數中的參數 | 建議 | 已獲悉 |
| 4 | - | 檢查 CakeRush 合約中的參數 | 建議 | 已修復 |
| 5 | - | 修飾符中的額外條件 | 建議 | 已獲悉 |
| 6 | - | 潛在的中心化風險 | 備註 | - |
詳細內容請參閱下文。
2.1 軟體安全
2.1.1 參數重置後潛在的狀態不一致
| 項目 | 描述 |
|---|---|
| 嚴重程度 | 低 |
| 狀態 | 已於版本 2 修復 |
| 引入版本 | 版本 1 |
描述 CakeRush 合約根據多個參數分發獎勵。以下函數允許項目維護者重置部分參數:
function resetMultiplier() external onlyOwner {
uint256 len = rewardMultiplier.length;
for (uint8 i = 0; i < len; ++i) {
rewardMultiplier.pop();
rewardTier.pop();
}
tierLength = 0;
}
function resetTimeWeighting() external onlyOwner {
uint256 len = weightedTime.length;
for (uint8 i = 0; i < len; ++i) {
weightedTime.pop();
weighting.pop();
}
weightLength = 0;
}
表 2.1:CakeRush.sol
然而,這些函數僅重置了參數,但未重置儲存在 userInfos 狀態變數中的用戶資訊。因此,CakeRush 合約中的計算可能會因為狀態不一致而失敗。例如,如果參數被重置並設定為錯誤的值,第 155 行的減法可能會因整數下溢(integer underflow)而失敗。
function quoteConvert(
uint256 _amountToConvert,
address _account
)
external
view
returns (
uint256 newUserFactor,
uint256 newTotalFactor,
uint256 newUserWeightedFactor,
uint256 newWeightedTotalFactor
)
{
if (_amountToConvert == 0 || rewardMultiplier.length == 0 || weighting.length == 0)
return (0, 0, 0, 0);
UserInfo storage userInfo = userInfos[_account];
uint256 accumulated = _amountToConvert + userInfo.converted;
uint256 factorAccuNoWeighting = 0;
uint256 i = 1;
while (i < rewardTier.length && accumulated > rewardTier[i]) {
factorAccuNoWeighting += (rewardTier[i] - rewardTier[i - 1]) * rewardMultiplier[i - 1];
i++;
}
factorAccuNoWeighting += (accumulated - rewardTier[i - 1]) * rewardMultiplier[i - 1];
uint256 factorToEarnNoWeighting = (factorAccuNoWeighting / DENOMINATOR) - userInfo.factor;
表 2.2:CakeRush.sol
更糟的是,如果用戶在參數重置後立即(在新參數設定前,例如通過搶先交易)呼叫 convert 或 convertWithCakePool,可能會因為第 141-142 行的邏輯導致合約內記錄的總因數和加權因數被重置。
影響 重置參數可能導致狀態不一致和錯誤。
建議 在清除舊參數後設定新參數。
項目方回應 一旦 Cake Rush 活動開始,乘數將不再重置。
2.1.2 重複領取 mCake 獎勵
| 項目 | 描述 |
|---|---|
| 嚴重程度 | 高 |
| 狀態 | 已於版本 3 修復 |
| 引入版本 | 版本 2 |
描述 用戶在合約中鎖定 CAKE 代幣後,可以通過該函數領取 mCake 代幣獎勵。然而,該函數包含一個允許用戶多次領取獎勵的問題。在以下程式碼片段中,如果金額大於用戶的金額,將會向用戶轉移或存入總金額。正確的實現應該僅返回金額,因此目前的實現實際上允許用戶重複領取 mCake 獎勵。
function claim(bool _isStake) external nonReentrant {
UserInfo storage userInfo = userInfos[msg.sender];
if (claimedMCake[msg.sender] >= userInfo.converted) revert AlreadyClaimed();
if (_isStake && userInfo.converted > 0) {
if (masterCakepie == address(0)) revert MasterCakepieNotSet();
IERC20(mCakeOFT).safeApprove(address(masterCakepie), userInfo.converted);
IMasterCakepie(masterCakepie).depositFor(
address(mCakeOFT),
address(msg.sender),
userInfo.converted
);
} else if (userInfo.converted > 0) {
IERC20(mCakeOFT).transfer(msg.sender, userInfo.converted);
emit Claim(msg.sender, userInfo.converted);
}
claimedMCake[msg.sender] = userInfo.converted;
}
表 2.3:CakeRush.sol
影響 用戶能夠重複領取 mCake 獎勵。
建議 修訂獎勵領取邏輯。
2.2 其他建議
2.2.1 檢查初始化函數中的參數
| 項目 | 描述 |
|---|---|
| 狀態 | 已獲悉 |
| 引入版本 | 版本 1 |
描述 在 CakeRush 和 PancakeStakingBNBChain 合約的初始化函數中,有些參數在初始化後無法更改。建議應在初始化函數中檢查這些參數。
function __CakeRush_init(
address _cake,
address _mCakeOFT,
address _masterCakepie
) public initializer {
__Ownable_init();
__ReentrancyGuard_init();
__Pausable_init();
cake = _cake;
mCakeOFT = _mCakeOFT;
masterCakepie = _masterCakepie;
}
表 2.4:CakeRush.sol
影響 N/A
建議 在初始化函數中檢查參數。
2.2.2 檢查 CakeRush 合約中的參數
| 項目 | 描述 |
|---|---|
| 狀態 | 已於版本 2 修復 |
| 引入版本 | 版本 1 |
描述 在 CakeRush 合約中,可以新增多個有關獎勵分發的參數。然而,沒有檢查這些參數是否按照合約假設正確設定。具體而言,在 setMultipler 和 setTimeWeighting 函數中,必須檢查額外條件(即 rewardTier 和 weightedTime 陣列的單調遞增特性)。
function setMultiplier(
uint256[] calldata _multiplier,
uint256[] calldata _tier
) external onlyOwner {
if (_multiplier.length == 0 || (_multiplier.length != _tier.length)) revert LengthInvalid();
for (uint8 i = 0; i < _multiplier.length; ++i) {
if (_multiplier[i] == 0) revert InvalidAmount();
rewardMultiplier.push(_multiplier[i]);
rewardTier.push(_tier[i]);
tierLength += 1;
}
}
表 2.5:CakeRush.sol
影響 N/A
建議 在設定參數的函數中檢查參數。
2.2.3 修飾符中的額外條件
| 項目 | 描述 |
|---|---|
| 狀態 | 已獲悉 |
| 引入版本 | 版本 1 |
描述 在 CakeRush 合約中,_onlyPancakeStaking 修飾符具有外加條件。根據此修飾符的語義,檢查 msg.sender != pancakeStaking 就已足夠。
modifier _onlyPancakeStaking() {
if (pancakeStaking == address(0) || msg.sender != pancakeStaking)
revert OnlyPancakeStaking();
_;
}
表 2.6:CakeRush.sol
影響 N/A
建議 移除修飾符中冗餘的條件。
2.3 備註
2.3.1 潛在的中心化風險
描述 CakeRush 的所有者擁有修改關鍵配置的重大特權,這導致了單點故障。如果攻擊者破壞了所有者權限,他們可能會使整個系統癱瘓。
此外,未明確處理合約中的 CAKE 代幣以將其鎖定在 VECake 合約中。相反地,CakeRush 允許所有者提取所有這些 CAKE,這意味著所有者必須在提取後鎖定 CAKE 代幣。然而,程式碼層級無法保證此邏輯,這也帶來了中心化疑慮。
項目方回應 團隊將所有者權限移交給多簽錢包以降低風險。
3. 注意事項與備註
3.1 免責聲明
本審計報告不構成投資建議或個人推薦。它並未考量,也不應被解釋為考量或對代幣、代幣銷售或任何其他產品、服務或其他資產的潛在經濟效益產生影響。任何實體不應以任何方式依賴本報告,包括用於做出購買或出售任何代幣、產品、服務或其他資產的決策。
本審計報告並非對任何特定項目或團隊的背書,報告也不保證任何特定項目的安全性。本審計不保證能夠發現智能合約的所有安全問題,即評估結果不保證未來不會發現進一步的安全問題。由於一次審計不能被視為全面性的,我們始終建議進行獨立審計並建立公開漏洞懸賞計畫,以確保智能合約的安全性。
本次審計範圍僅限於第 1.1 節中提到的程式碼。除非明確說明,否則語言本身(例如 Solidity 語言)、底層編譯工具鏈和計算基礎設施的安全性均不在審計範圍內。
3.2 審計程序
我們按照以下程序進行審計:
-
漏洞檢測 我們首先使用自動程式碼分析器掃描智能合約,然後手動驗證(拒絕或確認)其報告的問題。
-
語義分析 我們研究智能合約的業務邏輯,並使用自動模糊測試工具(由我們的研究團隊開發)對潛在漏洞進行進一步調查。我們還與獨立審計員手動分析可能的攻擊場景,以交叉檢查結果。
-
建議 我們從良好程式設計實踐的角度為開發人員提供一些有用的建議,包括 Gas 優化、程式碼風格等。
我們在下方展示了主要的檢查項目。
3.2.1 軟體安全
-
重入 (Reentrancy)
-
拒絕服務 (DoS)
-
存取控制
-
資料處理與資料流
-
異常處理
-
不安全的外部呼叫與控制流
-
初始化一致性
-
事件操作
-
容易出錯的隨機性
-
不當使用代理系統
3.2.2 DeFi 安全
-
語義一致性
-
功能一致性
-
權限管理
-
業務邏輯
-
代幣操作
-
緊急機制
-
預言機安全
-
白名單與黑名單
-
經濟影響
-
批次轉帳
3.2.3 NFT 安全
-
重複項目
-
代幣接收者驗證
-
鏈下元資料安全
3.2.4 其他建議
-
Gas 優化
-
程式碼品質與風格
備註 上述檢查項目為主要檢查項。根據項目的功能,我們在審計過程中可能會使用更多檢查項目。



