该文档是OpenZeppelin对Consensys/linea-tokens代码仓库的一次安全审计报告,涵盖了Linea L1和L2上的ERC-20代币合约以及一个L2上的空投合约。审计期间发现了一些低风险问题,如文档字符串不完整、代码优化空间和不确定的许可协议,但没有发现高危或严重漏洞。报告总结了审计范围、系统概述、安全模型、问题详情以及修复情况。
TypeLayer 2 & RollupsTimelineFrom 2025-07-22To 2025-07-24LanguagesSolidityTotal Issues10 (5 resolved, 1 partially resolved)Critical Severity Issues0 (0 resolved)High Severity Issues0 (0 resolved)Medium Severity Issues0 (0 resolved)Low Severity Issues3 (1 resolved, 1 partially resolved)Notes & Additional Information6 (3 resolved)
OpenZeppelin 审计了 Consensys/linea-tokens 仓库,提交版本为 44640f0。此外,pull rquest #10 审计至提交版本 86605a9,而 pull request #11 审计至提交版本 b9aad54。
更新: 我们已经完成了对所有提交内容的审查,并最终确定了审计报告。最终审查的提交版本为 91036da。我们注意到自 44640f0 以来,范围内文件的其他更改与和我们同时进行的审计结果相关。
此外,我们已经验证了已部署的字节码与 commit 91036da 的代码相匹配。
以下文件在审计范围内:
src
├── L1
│ ├── LineaToken.sol
│ └── interfaces
│ └── ILineaToken.sol
├── L2
│ ├── L2LineaToken.sol
│ └── interfaces
│ └── IL2LineaToken.sol
├── airdrops
│ └── TokenAirdrop.sol
└── interfaces
├── IGenericErrors.sol
└── IMessageService.sol
该系统包括两个部署在以太坊 L1 和 Linea L2 上的 ERC-20 代币合约,以及一个部署在 L2 上的空投合约。这些合约共同实现了跨链代币铸造、供应同步以及基于预定义的外部信号的结构化空投分配。
LineaToken
,是一个可升级的 ERC-20 合约,具有可燃烧的、基于许可的批准和基于角色的访问控制。它支持通过 MINTER_ROLE
进行受控铸造,并使用 Linea 的消息服务将总供应量更新传播到 L2 对应方。L2LineaToken
,是 L1 代币的桥接版本。它包括 ERC20VotesUpgradeable
,允许投票权委托以及与依赖于代币加权治理或基于身份的实用程序的应用程序集成。TokenAirdrop
合约部署在 L2 上,允许符合条件的用户认领 L2 代币的分配。它不是依赖于白名单或 Merkle 根,而是根据用户在最多三个外部合约中的余额动态计算分配。这些外部合约不代表可转让的代币,可能仅用作确定资格或按比例分配的参考点。该设计假设这些合约的余额对每个用户都是不可变的,这确保了空投的公平性以及对女巫攻击或重放攻击的抵抗力。这种结构支持安全、模块化和可升级的分配系统,该系统将 L1-L2 代币流与透明且可编程的空投逻辑连接起来。
在审计期间,做出了以下信任假设:
在空投中被引用为 PRIMARY_FACTOR_ADDRESS
、PRIMARY_CONDITIONAL_MULTIPLIER_ADDRESS
和 SECONDARY_FACTOR_ADDRESS
的合约必须满足特定要求,以确保分配的完整性:
balanceOf
功能的接口。PRIMARY_FACTOR_ADDRESS
和 SECONDARY_FACTOR_ADDRESS
必须与空投代币具有相同的小数位数。PRIMARY_CONDITIONAL_MULTIPLIER_ADDRESS
必须具有 9 个小数位,因为它用作乘数并除以 1e9 的固定分母。PRIMARY_FACTOR_ADDRESS
和 PRIMARY_CONDITIONAL_MULTIPLIER_ADDRESS
之间的余额乘法不得溢出 uint256
值。这一点尤其关键,因为在 这一行 中完成了乘法运算。与系统交互的交易应消耗少于 250,000 gas,以保持与 Linea 的消息服务基础设施的兼容性,并从 L1 → L2 消息的免费邮递员执行中受益。
部署和配置过程必须遵循特定的顺序以确保正确性:
setCustomContract
以将 L1 代币合约注册为相应 L2 代币的指定目标。confirmDeployment
函数,以通知主网桥 L2 上的部署已完成。这将更新 nativeToBridgedToken
映射以反映 DEPLOYED_STATUS
。此部署顺序保证了安全的空投初始化、一致的桥接逻辑以及 L1 和 L2 状态之间的安全同步。
在系统中识别出以下特权角色:
LineaToken
(L1) 定义了两个角色:
DEFAULT_ADMIN_ROLE
,用于管理角色和管理设置。MINTER_ROLE
,用于铸造新代币。L2LineaToken
仅接受来自 Linea 的官方代币桥和消息服务的消息。TokenAirdrop
由在部署时分配的单个所有者管理。在认领窗口关闭后,此所有者有权提取未认领的代币,并且可以在认领期结束后触发合约的 withdraw
函数。MessageServiceBase
修饰符(onlyMessagingService
、onlyAuthorizedRemoteSender
)强制执行跨链信任边界,确保只有经过身份验证的 L1/L2 消息服务和发送者才能调用跨链同步逻辑。在整个代码库中,发现了以下不完整的文档字符串实例:
IL2LineaToken.sol
中,在 L1LineaTokenTotalSupplySynced
事件中,l1BlockTimestamp
和 l1TotalSupply
参数未记录。考虑彻底记录作为合约公共 API 一部分的事件(及其参数)。编写文档字符串时,请考虑遵循 以太坊自然规范格式 (NatSpec)。
更新: 已在 pull request #16 中解决。
PRIMARY_FACTOR_ADDRESS
和 PRIMARY_CONDITIONAL_MULTIPLIER_ADDRESS
检查的优化TokenAirdrop
合约包含 这两个 require
语句。目的是确保 _primaryFactorAddress
和 _primaryConditionalMultiplierAddress
都已设置(非零)或都未设置(零)。这样可以防止仅初始化其中一个地址而未初始化另一个地址。但是,这两个条件在逻辑上是冗余的,可以简化。此外,根据构造函数强制执行的保证,还可以简化 calculateAllocation
函数中的后续逻辑。
考虑将 TokenAirdrop
构造函数中的两个 require
语句替换为单个等效条件,该条件确保两个地址都已设置或都未设置。此外,简化 calculateAllocation
中的条件以仅检查两个地址中的一个。
更新: 已在 pull request #10 中部分解决。虽然 calculateAllocation
中的检查已简化,但可以进一步简化构造函数中的 require
语句。
应修复 Pragma 指令以清楚地标识将编译合约的 Solidity 版本。
在整个代码库中,发现了多个浮动 pragma 指令的实例:
ILineaToken.sol
具有 solidity ^0.8.30
浮动 pragma 指令。IL2LineaToken.sol
具有 solidity ^0.8.30
浮动 pragma 指令。IGenericErrors.sol
具有 solidity ^0.8.30
浮动 pragma 指令。IMessageService.sol
具有 solidity ^0.8.30
浮动 pragma 指令。考虑使用固定的 pragma 指令。
更新: 已确认,但未解决。该团队表示:
已确认:这些故意保持开放状态,供其他人在未来/更高版本的合约中使用。顶层可部署文件将指示编译器版本,该版本已设置为没有浮动 pragma。预计不会有任何变化。
在 L2LineaToken
合约中,super.nonces
调用是模糊的。
考虑避免对父合约进行模糊调用,并明确指定正在调用哪个父合约的函数。
更新: 已确认,但未解决。该团队表示:
已确认:当删除
super.
并显式使用permit
时,投票 nonces 将被绕过。保持原样更安全。
在整个代码库中,发现了多个更新状态但没有事件发出的函数的实例:
LineaToken.sol
中的 initialize
函数L2LineaToken.sol
中的 initialize
函数TokenAirdrop.sol
中的 constructor
函数考虑在每次状态更改时发出事件,以提高代码库的清晰度并使其不易出错。
更新: 已在 pull request #16 和 pull request #10 中解决。
在整个代码库中,发现了多个事件没有索引参数的实例:
ILineaToken.sol
的 L2TokenAddressSet
事件ILineaToken.sol
的 L1TotalSupplySyncStarted
事件IL2LineaToken.sol
的 L1LineaTokenTotalSupplySynced
事件TokenAirdrop.sol
的 TokenBalanceWithdrawn
事件为了提高链下服务搜索和筛选特定事件的能力,请考虑 索引事件参数。
更新: 已确认,但未解决。该团队表示:
已确认。事件值本身是可变的(例如,
TokenBalanceWithdrawn
可以是任何值),不会通过该值进行事件查询。如果需要,添加索引将是明智的。这些不会进行任何更改。
selfdestruct
不会删除代码预期的行为是,调用 TokenAirdrop.sol
中的 withdraw()
函数应完全删除合约,将任何剩余的 ETH 转移给调用者,并防止进一步与合约地址交互。但是,由于 EIP-6780 中引入的行为更改,使用 selfdestruct(payable(msg.sender))
不再删除合约的字节码 — 它仅转移 ETH。
考虑用直接的 ETH 转移替换 selfdestruct
,以提高代码清晰度并更好地与 EIP-6780 之后的行为保持一致。
更新: 已在 pull request #12 中解决。该团队表示:
已确认:最初我们希望该调用发生在我们的网络上,同时应用伦敦 EVM 版本。这已被移除。
在整个代码库中,发现了多个文件具有犹豫不决的 SPDX 许可证的实例:
LineaToken.sol
中的 // SPDX-License-Identifier: Apache-2.0 OR MIT
SPDX 许可证ILineaToken.sol
中的 // SPDX-License-Identifier: Apache-2.0 OR MIT
SPDX 许可证L2LineaToken.sol
中的 // SPDX-License-Identifier: Apache-2.0 OR MIT
SPDX 许可证IL2LineaToken.sol
中的 // SPDX-License-Identifier: Apache-2.0 OR MIT
SPDX 许可证TokenAirdrop.sol
中的 // SPDX-License-Identifier: Apache-2.0 OR MIT
SPDX 许可证IGenericErrors.sol
中的 // SPDX-License-Identifier: Apache-2.0 OR MIT
SPDX 许可证IMessageService.sol
中的 // SPDX-License-Identifier: Apache-2.0 OR MIT
SPDX 许可证考虑仅指定一个许可证,以防止可能的许可模糊。
更新: 已确认,但未解决。该团队表示:
已确认:这样做是为了让我们的代码的消费者在将其合并到其代码库中时具有灵活性。目的是应用他们的代码库使用的任何一个。
在 LineaToken.sol
中,emit L1TotalSupplySyncStarted(block.timestamp, totalSupply);
事件发出包含不必要的数据字段,例如 block.number
或 block.timestamp
。
为了提高代码清晰度和可维护性,请考虑从事件发出中删除不必要的数据字段(如 block.number
或 block.timestamp
),因为它们已包含在区块信息中。
更新: 已在 pull request #15 中解决。该团队表示:
已确认:最初是为了清晰起见,但是,删除它是更好的做法,并且已经完成。
L2LineaToken
不需要 ERC20BurnableUpgradeable
继承L2LineaToken
合约包含 burn
函数 的自定义实现,该实现具有特定于 Linea 代币桥的额外授权逻辑。因此,从 ERC20BurnableUpgradeable
继承是不必要的,并且引入了代币未使用的额外功能。
Linea 团队在并行审计期间也发现了此问题,并且在 commit b9aad54 中删除了从 ERC20BurnableUpgradeable
的继承。
更新: 已在 commit b9aad54 中解决。
经过审计的系统支持 Linea 代币生成事件 (TGE),从而实现跨链代币铸造、供应同步和空投分配。它包括一个桥接的 ERC-20 代币对和一个空投合约,该合约根据外部参考合约中的余额计算分配。
未发现高风险或严重风险问题。仅报告了轻微的观察结果,主要与处理特定边缘案例和编写文档有关。桥接流程的正确性以及围绕分配输入的假设对于系统的完整性至关重要,应在部署期间仔细监控。
感谢 Linea 团队在整个审计过程中做出的积极响应和合作。
- 原文链接: blog.openzeppelin.com/li...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!