
OpenSea 使用了 Wyvern 协议。协议内容,主要包括了代理注册部分和交换部分。本文主要展开分析 OpenSea 所使用的 代理注册部分。
1. 概述
从技术层面讲,整个代理注册过程,关键在于通过 代理模式 实现 可更新的授权实现者 。
所谓 授权实现者,即,在 NFT 合约中,用户授权其可以转移自己的 NFT。授权实现者 被用户 approve 后,通过 delegatecall 或者 call 调用可以代替用户处理 NFT。
所谓 可更新性,即,通过重新部署新合约的形式,借助代理模式,将 授权实现者 指向最新的合约。这种方式,将大大增强合约的灵活性。
接下来,就具体合约内容做分析。
继承关系如下所示:
2. Proxy
代理过程,通过 fallback 函数实现,fallback 中,通过内联汇编,实现可以接收返回值的 delegatecall。
汇编代码中,最终调用 implementation 做实现。而该变量,在 Proxy 构造时,被构造参数赋值为 AuthenticatedProxy。
当然,implementation 是可以被“升级”的。即,允许重新上链一个合约作为 implementation。
1 | function () payable public { |
3. AuthenticatedProxy
AuthenticatedProxy 是事实上的授权实现者。虽然,用户将交易权授权给了 OwnableDelegateProxy,但是,其 通过 delegatecall 最终调用了 AuthenticatedProxy 中的 proxy 方法。delegatecall 调用机制,从某种程度上就像 OwnableDelegateProxy 继承了 AuthenticatedProxy。
1 | function proxy(address dest, HowToCall howToCall, bytes calldata) public returns (bool result) |
注意,在注册中心 registry,已经对交易协议 Wyvern Exchange v2 进行了授权。因此,当调用者为 Wyvern Exchange v2 时,registry.contracts(msg.sender)) 将返回 true。Wyvern Exchange v2 合约地址为 0x7f268357a8c2552623316e2562d90e642bb538e5,可以使用该地址,在 OwnableDelegateProxy 中检查授权。
4. ProxyRegistry
主要包括两个功能:
- 管理
proxy方法的调用授权。 - 为调用者创建代理对象。
4.1 授权管理
需要注意的是,在授权第三方可以请求代理方法执行时,授权过程需要两周才会生效。而取消一个授权,是可以直接取消的。
另外,授权管理方法,只能由 ProxyRegistry 的创建者调用。
1 | function startGrantAuthentication (address addr) public onlyOwner{ |
1 | // 取消授权 |
在授权时,只有一种不需要 两周时延 的方法,但是只能调用一次。
1 | function grantInitialAuthentication (address authAddress) onlyOwner public |
授权的本质,是 registry.contracts(msg.sender) 布尔值的赋值。根据该值,将决定是否被允许可以进行 AuthenticatedProxy 中 proxy 调用。proxy 函数中,将对被代理对象执行 delegatecall,通过该方法,完成 NFT 所属权转移。
4.2 创建代理
ProxyRegistry 会为用户创建 OwnableDelegateProxy 合约。OwnableDelegateProxy 合约是用户授权的对象,它有权限转移用户的 NFT。
1 | function registerProxy() public returns (OwnableDelegateProxy proxy) |
5. 代理对象 OwnableDelegateProxy
最终,为用户注册的合约为 OwnableDelegateProxy.
其构造方法为:
1 | constructor(address owner, address initialImplementation, bytes calldata) |
1 | // 调用了 AuthenticatedProxy 中的 `initialize` |
6. WyvernProxyRegistry
注意,其继承自 ProxyRegistry,在单独的 WyvernProxyRegistry 层面,完成了两件事:
在构造函数中,创建
AuthenticatedProxy,该对象将传递给各个由WyvernProxyRegistry创建的OwnableDelegateProxy1
2
3
4constructor () public
{
delegateProxyImplementation = new AuthenticatedProxy();
}可以进行一次没有延时的合约授权
1
2
3
4
5
6function grantInitialAuthentication (address authAddress) onlyOwner public
{
require(!initialAddressSet);
initialAddressSet = true;
contracts[authAddress] = true;
}
7. OwnedUpgradeabilityProxy
最后,说明下管理代理更新的合约。
主要包含两个功能:谁有权限可以更新实现;当前的实现是谁。
1 | function transferProxyOwnership(address newOwner) public onlyProxyOwner { |
1 | function _upgradeTo(address implementation) internal { |