本文介绍了如何使用 safe-utils 和 tenderly-utils 这两个 Foundry 模块来简化协议的治理流程,尤其是在涉及 Safe 多签账户的场景下。
治理提案的正确方式
探讨 safe-utils 和 tenderly-utils 如何帮助简化多重签名交易提案和模拟。如果你的 DAO 或协议不断地将更新推送到生产环境,本文将对你有所帮助。
在构建可升级或可配置的协议时,一种常见的设置是将治理权委托给 Safe 智能账户,可能位于 时间锁或 Governor 合约 之后。
一方面,让多个签名者审查配置或协议更新可以减少风险,因为这样可以让更多人检查提议的更改。另一方面,大多数协议仍然依赖 Safe UI 来提出这些交易,这对于复杂的更新来说可能很麻烦,并且很难让受信任的各方审查。
为了缓解这些缺点,我们推出了两个 Foundry 模块:safe-utils 和 tenderly-utils,它们可以用最少的设置来帮助简化此过程。这些库可以单独使用或一起使用,在本文中,我们提出了一个框架来改进协议的治理操作。
使用 safe-utils 和 tenderly-utils 自动化你的安全操作
safe-utils
safe-utils
库支持从 Foundry 脚本向 Safe API 提出多重签名交易。
forge install Recon-Fuzz/safe-utils
import {Safe} from "safe-utils/Safe.sol";
using Safe for *;
Safe.Client safe;
function setUp() public {
safe.initialize(vm.envAddress("SAFE_ADDRESS"));
}
safe.proposeTransaction(
target,
abi.encodeCall(Contract.functionName, (args)),
signer
);
该提案经过签名并发布到 Safe API 后端,以便稍后由其他 Safe 所有者批准。通过将提案实现为 Foundry 脚本,安全研究人员可以帮助审查、发现潜在问题并减轻风险。
tenderly-utils
tenderly-utils
库提供了一个与 Tenderly API 的接口,用于自动化模拟和虚拟测试网创建。 它可以与 safe-utils
模块结合使用,以便提案可以立即应用于 Fork 网络。 这非常有用,前端和后端团队可以在治理更新生效之前立即对其进行测试。
forge install Recon-Fuzz/tenderly-utils
import {Tenderly} from "tenderly-utils/Tenderly.sol";
using Tenderly for *;
Tenderly.Client tenderly;
function setUp() public {
tenderly.initialize(
vm.envString("TENDERLY_ACCOUNT_NAME"),
vm.envString("TENDERLY_PROJECT_NAME"),
vm.envString("TENDERLY_ACCESS_KEY")
);
}
Tenderly.VirtualTestnet memory vnet =
tenderly.createVirtualTestnet("vnet", block.chainid);
Tenderly.Transaction memory transaction = tenderly.sendTransaction(
vnet.id, from, weth, abi.encodeCall(IWETH.withdraw.selector, (amount))
);
虚拟测试网是一个你可以与你的团队共享的链 Fork,它反映了模拟状态。 对于多重签名提案,这非常有用,因为你还可以使用像 setStorageAt
这样的作弊码将 Safe 阈值更改为 1,并立即让治理提案通过。 这正是你点击 “Simulate transaction” 时通过 Safe UI 发生的事情。
在 Size Credit(一个具有统一流动性的固定利率借贷市场)中,这些库的使用方式如下:
过去,新的货币市场是通过管理前端部署的。 这很难维护,因为它需要频繁的安全更新以最大程度地减少用户输入错误,而这些错误非常频繁。 现在,部署使用 Foundry 脚本,该脚本更易于审查和扩展。 可以通过获取市场注册表并仅更新一些参数(例如抵押品/债务代币和价格 Feed)来克隆现有配置,从而降低人为错误的风险。
ProposeSafeTxDeployPTMarkets.s.sol
在底层,proposeTransaction 函数对操作进行签名,并使用 Safe execTransaction 方法参数向 Safe API Kit 后端提交 HTTP 请求。
协议更新通常涉及将批量交易提交到多个地址。 以前,这是通过 Safe 前端的“Batch Transaction”工具完成的。 这非常容易出错且耗时,因为它有很多手动步骤:复制合约地址,复制 ABI,复制更新参数,确保它们已正确格式化,然后对所有必要的更改重复执行此操作。 使用 safe-utils
可以通过编程方式构造此操作,从而防止人为错误,例如忘记更新一个市场、将更新提交到无效地址以及其他错误,所有这些错误已经在生产中发生过。
ProposeSafeTxUpdateConfig.s.sol
在实现方面,proposeTransactions (复数,因为它可用于传递多个子调用)辅助函数将使用与底层的 Safe SDK 相同的 MultiSendCallOnly 批量聚合器合约。 使用 safe-utils 库而不是手动调用 MultiSendCallOnly
的好处是,它提供了一个更简单的接口,因为开发人员无需手动构造 multiSend
参数,因为它具有非常特定的编码。 一个重要的底层细节是,必须通过 DelegateCall
进行 execTransaction
operation
参数,以确保交易上下文(包括 msg.sender
)在交易链中的所有子调用中都保持为 Safe 帐户。
在部署新的货币市场之前,协议需要进行多项分析以确定风险参数,例如抵押率和清算奖励。 使用不同的配置执行实际清算可以帮助更全面地了解价格对 DEX 的影响以及流动性如何受到价格变化的影响。
在 tenderly-utils
的帮助下创建的 Tenderly 虚拟测试网可以成为模拟借款、价格下跌和清算事件的有用工具。 然后,清算引擎可以使用闪电贷来偿还借款并评估此 Fork 网络上的总体输出,然后根据调查结果调整治理参数。
SimulateBorrowerLiquidatable.s.sol
正如你可以想象的那样,这些 API 触发器在底层是经过美化的 HTTP 调用,使用 Foundry 的 vm.serialize*
正确编码,并使用 vm.parseJson
解码。 为了实现这些请求,开发了一个轻量级的 solidity-http 客户端库,其灵感来自 axios,可以通过 ffi
轻松访问 curl
。
通过结合 safe-utils
和 tenderly-utils
,我们提出了一个脚本化完整 Safe 交易生命周期的框架:
编码批量或单笔交易
向 Safe API 提出交易
通过 Tenderly Fork 模拟执行并覆盖阈值
与你的团队共享虚拟测试网
此设置已为 DAO 工作流程、协议治理和多重签名操作做好准备。 如果你有兴趣让受信任的合作伙伴审查你的治理操作,可以联系 Recon 。
- 原文链接: getrecon.substack.com/p/...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!