工厂克隆模式在最小化 gas 成本方面可能是有利的。然而,由于每个克隆都被部署到一个新的地址,因此有效地跟踪和监控这些合约可能是一个挑战。
本指南展示了如何使用 Defender 来监控工厂合约以及由它创建的克隆合约。监控自动化是通过以下 Defender 模块结构实现的:
-
一个 Monitor 监视由创建克隆的工厂合约发出的成功事件。如果检测到,它会触发一个 Action 并 传递关于交易的信息。
-
该 Action 利用
defender-sdk
,window=_blank 将新创建的合约地址添加到 地址簿 中,以便更轻松地进行监控。 -
此外,该 Action 使用
defender-sdk
,window=_blank 将克隆地址添加到 Monitor 正在监视的地址列表中。
在这种情况下,可以预先提供合约 ABI,因为克隆合约将具有相同的 ABI。或者,您可以使用 Etherscan 的 API,window=_blank 从给定地址的已验证合约动态检索 ABI。
生成 API 密钥
要以编程方式将合约添加到地址簿,https://www.npmjs.com/package/@openzeppelin/defender-sdk[sdk
,window=_blank] 需要 API 密钥和密钥形式的凭据。在 API 密钥页面,window=_blank 中创建并复制凭据。

现在,导航到 Defender 中的 Secrets,window=_blank 页面,创建一个名为 API_KEY
的新密钥,然后粘贴 API 密钥。创建另一个名为 API_SECRET
的密钥,然后粘贴 API 密钥。这些密钥将由 Action 安全地使用。

创建 Action
导航到 Action 创建页面,window=_blank,输入名称,然后选择 Webhook
作为触发器。然后,粘贴以下 Action 代码并保存它:
const { Defender } = require('@openzeppelin/defender-sdk');
exports.handler = async function (event) {
const creds = {
apiKey: event.secrets.API_KEY,
apiSecret: event.secrets.API_SECRET,
}
const client = new Defender(creds);
const payload = event.request.body
const matchReasons = payload.matchReasons
const newCloneAddress = matchReasons[0].params._clone
const newCloneAbi = `[
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "ValueChanged",
"type": "event"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "initialize",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "retrieve",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "store",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]`
// 添加新的克隆合约
await client.proposal.addContract({
network: 'sepolia',
address: newCloneAddress,
name: `Clone ${newCloneAddress}`,
abi: newCloneAbi,
})
}

该 Action 现在已准备好被 Monitor 触发。
手动触发此 Action 将引发错误,因为 Action 依赖于 Monitor 提供的数据(例如新部署的克隆合约地址)。 |
创建 Monitor
此 Monitor 将监视工厂合约发出的指示已创建新克隆的事件。导航到 Monitor 创建页面,window=_blank,选择一个名称、风险类别,然后选择 Factory 合约(如果尚未添加,则添加工厂)。

保留 Transaction Filters
不变,然后继续到 Events
选项卡。在此处,选择用于克隆创建的事件名称,并将事件参数留空以捕获所有发出的事件。

最后,打开 Alerts
部分,然后在 Execute an Action
下拉列表中选择在上一步中创建的 Action。随意添加任何其他设置(如通知),然后保存 Monitor。

与任何 action 一样,此 Monitor 的触发将记录在 日志 中。
测试运行
要测试设置,请导航到 Transaction Proposals,window=_blank 以通过工厂手动创建克隆。选择工厂合约,然后调用使用任何所需参数创建克隆的函数。

然后,使用您喜欢的审批流程(如 Relayer 或 EOA 钱包)执行此交易。转到 Action 的运行历史记录以验证它是否由 Monitor 触发,并将克隆合约地址添加到 Defender。

为克隆创建 Monitor
现在您有一个克隆合约可以用作所有未来克隆合约的模板,是时候为它们创建一个 Monitor 了。导航到 Monitor 创建页面,window=_blank,选择一个名称、风险类别,然后选择克隆合约。
此外,您可以随意为交易、事件和函数或通知添加任何其他过滤器。保存 Monitor 并观察日志/通知,以验证 Monitor 是否按预期工作。

自动将克隆添加到 Monitor
使用上一个 Monitor,您可以更新 Action 以将任何新创建的合约添加到 Monitor 正在监视的地址列表中。使用以下代码更新 Action 代码,将 monitorId
替换为在上一步中创建的 Monitor 的 ID:
const { Defender } = require('@openzeppelin/defender-sdk');
exports.handler = async function (event) {
const creds = {
apiKey: event.secrets.API_KEY,
apiSecret: event.secrets.API_SECRET,
}
const client = new Defender(creds);
const payload = event.request.body
const matchReasons = payload.matchReasons
const newCloneAddress = matchReasons[0].params._clone
const newCloneAbi = `[
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "ValueChanged",
"type": "event"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "initialize",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "retrieve",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "store",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]`
// 添加新的克隆合约
await client.proposal.addContract({
network: 'sepolia',
address: newCloneAddress,
name: `Clone ${newCloneAddress}`,
abi: newCloneAbi,
})
// 将克隆合约添加到 Monitor
const monitorId = 'REPLACE'
const monitor = await client.monitor.get(monitorId)
const subscribedAddresses = monitor.addressRules[0].addresses
subscribedAddresses.push(newCloneAddress)
await client.action.update(monitorId, { addresses: subscribedAddresses })
}
现在,当 Action 运行时,它不仅会将合约添加到 Defender,还会将其添加到 Monitor。
要验证,请执行另一次测试运行!