Beacon Proxy Pattern
- 原文链接:https://www.rareskills.io/post/beacon-proxy
- 译者:AI翻译官,校对:翻译小组
- 本文永久链接:learnblockchain.cn/article…
Beacon Proxy(信标代理)是一种智能合约升级模式,其中多个代理使用相同的实现合约,并且所有代理可以在单个交易中升级。本文解释了这种代理模式的工作原理。
我们假设你已经了解了最小代理的工作原理,甚至可能了解 UUPS 或 透明代理。
通常,代理模式使用单个实现合约和单个代理合约。然而,多个代理也可以使用相同的实现。
为了理解为什么我们需要这样做,让我们想象一个完全在链上的游戏。这个游戏希望将每个用户账户存储为一个单独的合约,以便账户可以轻松转移到不同的钱包,并且一个钱包可以拥有多个账户。每个代理在其各自的存储变量中存储账户信息。
你可以通过以下几种方式实现这一点:
使用最小代理标准(EIP1167)并将每个账户部署为克隆
使用 UUPS 或透明代理模式并为每个账户部署一个代理
在大多数情况下,任何一种选择都可以工作,但如果你想为账户添加新功能怎么办?
在最小代理标准的情况下,你将不得不重新部署整个系统并进行社会迁移,因为克隆是不可升级的。
传统代理是可升级的,但你必须一个一个地升级每个代理。对于更多账户来说,这将是昂贵的。
当有很多克隆和代理时,升级它们都是一件麻烦事。
Beacon 模式旨在解决这个问题:它允许你部署一个新的实现合约并同时升级所有代理。
这意味着 Beacon 模式将允许你部署一个新的账户实现并一次性升级所有代理。
从高层次来看,这个标准允许你为每个实现合约创建无限数量的代理,并且仍然能够轻松升级。
顾名思义,这个标准需要一个 Beacon,OpenZeppelin 称之为“UpgradeableBeacon”,并在 UpgradeableBeacon.sol
中实现。
Beacon 是一个智能合约,通过公共函数向代理提供当前的实现地址。 Beacon 是代理关于当前实现地址的真实来源,这就是为什么它被称为“Beacon”。
当代理收到一个传入交易时,代理首先调用 Beacon 上的 view
函数 implementation()
以获取当前的实现地址,然后代理 delegatecalls
到该地址。这就是 Beacon 作为实现来源的工作原理。
任何额外的代理将遵循相同的模式:它们首先使用 implementation()
从 Beacon 获取实现地址,然后 delegatecall
到该地址。
注意:代理知道在哪里调用 implementation()
是因为它们在一个不可变变量中存储了 Beacon 的地址。我们稍后会详细解释这个机制。
这种模式具有高度可扩展性,因为每个额外的代理只需从 Beacon 读取实现地址,然后使用 delegatecall
。
尽管 Beacon Proxy 模式涉及更多的合约,但代理本身比 UUPS 或透明可升级代理更简单。
Beacon 代理总是调用相同的 Beacon 地址以获取当前的实现地址,因此它们不需要关心诸如管理员是谁或如何更改实现地址等细节。
由于所有代理从 Beacon 的存储中获取实现地址,更改存储槽中的地址会导致所有代理 delegatecall
到新地址,立即“重新路由”它们。
要同时升级所有代理:
部署一个新的实现合约
在 Beacon 的存储中设置新的实现地址
设置新的实现地址是通过调用 Beacon 上的 upgradeTo(address newImplementation)
并传递新地址作为参数来完成的。upgradeTo()
是 UpgradeableBeacon.sol
(Beacon)上的两个公共函数之一。另一个公共(视图)函数是我们之前提到的 implementation()
。
注意:upgradeTo()
具有一个 onlyOwner
修饰符,该修饰符在 UpgradeableBeacon.sol
(Beacon)的构造函数中设置。
upgradeTo()
调用一个内部函数 _setImplementation(address newImplementation)
(也在 Beacon 上),该函数检查新的实现地址是否是一个合约,然后将 Beacon 中的地址存储变量 _implementation
设置为新的实现地址。
现在 Beacon 存储中的实现地址已更改,所有代理将读取 Beacon 中的新地址并将其 delegatecall
路由到新的实现...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!