# Delegate Cash 合约分析

By [rayjun.eth](https://paragraph.com/@rayjun-2) · 2023-02-20

---

1.

最近猴子游戏 **Dookey Dash** 很火，这个游戏可以看成神庙逃亡的下水道版本，跑得越远，分数越高。但是很多人手残（比如我），很难玩出高的分数，于是游戏代打很自然的出现的了。

但是有个难处理的细节，代打的人如何获取到门票的授权？总不能直接把钱包给出去或者把游戏门票（Sewer Pass）转出去，这样太不安全了。在这里的关键就是怎么去证明你拥有这个资产的所有权，但是又不威胁到资产的安全。

在这个过程中，我发现有一个合约很好地解决了这个问题，就是这个 Delegate Cash。这个合约可以解决很多场景下资产所有权的问题：

*   比如上面的代打场景
    
*   保护资产安全，在加群或者领空投时，不想直接操作钱包
    

![Delegate Cash](https://storage.googleapis.com/papyrus_images/bc437d8fe40be599775fc318c3e8e5c4a6bf598daa309bcbc3df69c7389a5de5.png)

Delegate Cash

整个流程就是在 Delegate Cash 合约，你可以把自己的地址 A 代理给另外一个地址 B，B 拿到这个代理之后，能证明这些资产是属于你的，但是**同时 B 合约无法操作 A 合约中的任何资产**。这点很重要，这样即使 B 合约被钓鱼了或者私钥泄漏了，地址 A 中的资产也是安全的。

2.

Delegate Cash 有三种代理模式：

*   代理一个 token
    
*   代理一个合约地址
    
*   代理整个地址
    

    // 代理整个地址
    function delegateForAll(address delegate, bool value) external;
    // 代理某个合约地址
    function delegateForContract(address delegate, address contract_, bool value) external;
    // 代理一个 token
    function delegateForToken(address delegate, address contract_, uint256 tokenId, bool value) external;
    

用户所有的代理信息都被存储在下面两个变量中：

    mapping(address => EnumerableSet.Bytes32Set) internal delegationHashes;
    mapping(bytes32 => IDelegationRegistry.DelegationInfo) internal delegationInfo;
    

`delegationHashes` 中存储的是相关代理信息（比如代理地址、被代理地址、被代理的合约或者 token）算出来的 hash， `delegationInfo`中存储的是这些信息的原文。

绑定代理和解除代理都是通过下面这个方法来实现:

    function _setDelegationValues(
            address delegate,
            bytes32 delegateHash,
            bool value,
            IDelegationRegistry.DelegationType type_,
            address vault,
            address contract_,
            uint256 tokenId
        ) internal {
            if (value) {
                delegations[vault][vaultVersion[vault]].add(delegateHash);
                delegationHashes[delegate].add(delegateHash);
                delegationInfo[delegateHash] =
                    DelegationInfo({vault: vault, delegate: delegate, type_: type_, contract_: contract_, tokenId: tokenId});
            } else {
                delegations[vault][vaultVersion[vault]].remove(delegateHash);
                delegationHashes[delegate].remove(delegateHash);
                delete delegationInfo[delegateHash];
            }
        }
    

如果 value 为 true，则是绑定代理，如果 value 为 false，则是解除代理。然后还提供了一系列的查询方法来检查代理关系是否存在。

整体的逻辑就这些，相当简单。但是还存在一个小问题，如果在使用的过程中，绑定了很多代理关系，一个个去解绑就会浪费很多 gas，所以还需要一个一件解绑所有代理的功能，Delegate Cash 的实现相当巧妙。

引入了下面两个 version 的 map，其中 valutVersion 记录的是地址的版本，在当前的合约实现中，这个 map 的值的返回值一直是 0，delegateVersion 中记录的是当前这次代理的版本。

    mapping(address => uint256) internal vaultVersion;
    mapping(address => mapping(address => uint256)) internal delegateVersion;
    

这两个 Map 中存储的版本信息在每次算代理信息 hash 的时候会被用到：

    function _computeAllDelegationHash(address vault, address delegate) internal view returns (bytes32) {
            uint256 vaultVersion_ = vaultVersion[vault];
            uint256 delegateVersion_ = delegateVersion[vault][delegate];
            return keccak256(abi.encode(delegate, vault, vaultVersion_, delegateVersion_));
    }
    

所以在取消对某个地址的全部代理时，只需要对这个版本号加 1：

    function _revokeDelegate(address delegate, address vault) internal {
            ++delegateVersion[vault][delegate];
    }
    

这样就会让后续检查代理时的 hash 对比都会失败，对应的代理关系也全部失效：

     if (delegationHash == _computeAllDelegationHash(vault, delegationInfo_.delegate)) {
        //....
     }
    

后续在其他的 DAPP 中，如果要检查用户是否拥有某个资产，只需要来检查这个代理关系就可以:

    address requester = msg.sender;
      
    if (_vault != address(0)) { 
         bool isDelegateValid = dc.checkDelegateForContract(msg.sender, _vault, NFT_CONTRACT);
         require(isDelegateValid, "invalid delegate-vault pairing");
         requester = _vault;
     }
    

3.

通过这样的一个简单的合约，就可以在安全的情况下来证明用户是否拥有某个资产。这个也符合链上资产使用习惯，在大多数场景中，只需要证明资产是自己的，但很多人的资产就是在这个证明的过程中丢的。

这个合约对 gas 的消耗很大，在使用的过程中，要尽量减少代理和取消代理的次数。

**参考链接**

\[1\][https://delegate.cash/](https://delegate.cash/)

\[2\][https://etherscan.io/address/0x00000000000076A84feF008CDAbe6409d2FE638B#code](https://etherscan.io/address/0x00000000000076A84feF008CDAbe6409d2FE638B#code)

---

*Originally published on [rayjun.eth](https://paragraph.com/@rayjun-2/delegate-cash)*
