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
创建的OwnableDelegateProxy
1
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 { |