按照EIP-712規範簽名完成委託和投票

買賣虛擬貨幣
Compound[4]的治理體系是由發放給使用者的COMP代幣[5]來驅動的。COMP代幣持有者擁有與持有量1:1的投票權。投票權利可以委託給任意一個地址,讓其去給提案投票。使用者可以透過兩種方式委託投票或對提案進行投票:可以直接呼叫函式(delegate, castVote)或透過簽名功能函式(delegateBySig, castVotebySig)。透過簽名[6]功能函式的好處是使用者可以免費完成委託或投票交易,同時會有可信的第三方花費gas[7]費用將投票結果寫到區塊鏈中。在本次教程中,我們重點展示這類函式的例子。使用簽名實現委託按照EIP-712[8] 規範定義的結構化資料簽名方式,COMP代幣[9]持有者可以委託給任何一個以太坊地址。任何使用者只要有已簽名的委託交易,都可以呼叫COMP智慧合約中delegateBySig 函式[10]這種方式的使用場景可能是,一個委託者希望聯合其他COMP持有者將他們的投票委託給被委託人,並希望以非常低的成本來完成這項工作。
被委託者可以建立一個網頁,讓使用者透過Metamask和私鑰完成delegateBySig 交易,這樣被委託者就能收集到簽名資訊。之後,被委託者可以將簽名資訊打包,批次一次寫入到以太坊中,再執行delegateBySig函式就可以正式的收集到使用者的投票權利。透過簽名投票同delegateBySig一樣,使用者也可以委託第三方給 Compound治理提案[11]投票。任何使用者只要有已簽名的委投票交易,都可以呼叫智慧合約中castVoteBySig 函式[12]第三方提交使用者簽名交易和delegateBySig的情況是一樣的,但是投票權利僅限於一個提案,並非無限制的提案。在第三方正式將投票交易傳送到以太坊之前,原有的使用者依然保留自主投票的權利。在Web3頁面中使用簽名實現委託

使用此程式碼[13],任何人可以建立一個讓使用者使用簽名來委託投票權利的的網頁,我們假定訪問此頁面的所有使用者都使用MetaMask[14]來呼叫Web3函式。

當一個使用者訪問這個頁面時,他們可以看到自己選中的錢包[15]地址和預設的Compound治理地址。他們可以將被委託人的地址填到這個地址中。在實際應用中,這個地址可以固定寫成你要委託的目標地址。

接下來,使用者會點選“Create Delegation Signature(建立委託簽名)“按鈕,這個會觸發Metamask執行資料簽名。MetaMask的文件裡有關於資料簽名[16]的詳細介紹。

在使用者點選按鈕“SIGN(簽名)”時,會執行如下觸發事件:

sign.onclick = async () => {
  const _delegatee = delegateTo.value;
  const _nonce = await comp.methods.nonces(myAccount).call();
  const _expiry = 10e9; // expiration of signature, in seconds since unix epoch 以時間戳樣式的簽名過期時間
  const _chainId = web3.currentProvider.networkVersion;
  const msgParams = createDelegateBySigMessage(compAddress, _delegatee, _expiry, _chainId, _nonce);
  web3.currentProvider.sendAsync({
    method: 'eth_signTypedData_v4',
    params: [ myAccount, msgParams ],
    from: myAccount
  }, async (err, result) => {
    if (err) {
      console.error('ERROR', err);
      alert(err);
      return;
    } else if (result.error) {
      console.error('ERROR', result.error.message);
      alert(result.error.message);
      return;
    }
    const sig = result.result;
    delegatee.value = _delegatee;
    nonce.value = _nonce;
    expiry.value = _expiry;
    signature.value = sig;
    console.log('signature', sig);
    console.log('msgParams', JSON.parse(msgParams));
  });
};

程式碼中使用了Metamask[17]自帶eth_signTypedData_v4函式[18]。這個函式可以按照EIP-712規範的完成結構化資料簽名[19]。完成一次有效的簽名,需要3個引數

1. 被委託人以太坊地址
2. 使用簽名賬戶從COMP智慧合約中獲得的的nonce
3. 以時間戳樣式的交易過期時間

這個結構化資料簽名函式可以接受簽名地址加上JSON格式的字串。EIP-712規範定義了需要簽名的資料的types, struct和domain。這個實現在一個簡單的函式里,在按鈕觸發事件發生時會被呼叫。

const createDelegateBySigMessage = (compAddress, delegatee, expiry = 10e9, chainId = 1, nonce = 0) => {
  const types = {
    EIP712Domain: [
      { name: 'name', type: 'string' },
      { name: 'chainId', type: 'uint256' },
      { name: 'verifyingContract', type: 'address' },
    ],
    Delegation: [
      { name: 'delegatee', type: 'address' },
      { name: 'nonce', type: 'uint256' },
      { name: 'expiry', type: 'uint256' }
    ]
  };
  const primaryType = 'Delegation';
  const domain = { name: 'Compound', chainId, verifyingContract: compAddress };
  const message = { delegatee, nonce, expiry };
  return JSON.stringify({ types, primaryType, domain, message });
};

任何以太坊地址都可以使用被委託人地址,nonce[20],過期時間和簽名來發布委託交易。這些是呼叫COMP合約的delegateBySig函式需要的引數。獲得的簽名需要分成3個引數,命名為 v, r 和 s。

const sig = signature.value;
const r = '0x' + sig.substring(2).substring(0, 64);
const s = '0x' + sig.substring(2).substring(64, 128);
const v = '0x' + sig.substring(2).substring(128, 130);

根據EIP-712實現委託的完整程式碼[21]可以在compound治理樣例程式碼倉庫[22]中檢視。

在Web3網頁中透過簽名投票

和委託一樣,投票也可以建立一個網頁來完成。使用者在委託投票權利的時候,“贊同”和“反對”的委託需要分開傳送交易,第三方可以選擇最終釋出哪一個委託。

const createVoteBySigMessage = (govAddress, proposalId, support, chainId = 1) => {
  const types = {
    EIP712Domain: [
      { name: 'name', type: 'string' },
      { name: 'chainId', type: 'uint256' },
      { name: 'verifyingContract', type: 'address' },
    ],
    Ballot: [
      { name: 'proposalId', type: 'uint256' },
      { name: 'support', type: 'bool' }
    ]
  };
  const primaryType = 'Ballot';
  const domain = { name: 'Compound Governor Alpha', chainId, verifyingContract: govAddress };
  support = !!support;
  const message = { proposalId, support };
  return JSON.stringify({ types, primaryType, domain, message });
};

點選“Create Vote Signatures(建立投票簽名)”之後,會彈出讓使用者選擇簽名“贊成”和“反對”交易。與delegateBySig函式一致,透過傳遞2個引數來呼叫castVoteBySig函式。

1. Compound治理提案唯一編號(自增的整型)
2. 布林值來表示贊成還是反對提案

另外使用者還要傳入分成3部分的簽名,通常寫作為v, r和s

按照EIP-712規範投票完整頁面程式碼[23]可以在compound治理樣例程式碼倉庫[24]檢視。

透過指令碼或者智慧合約打包和釋出簽名

一旦使用者收集完Compound治理的已簽名的交易後,他們需要將這些交易釋出到以太坊上。實現批次傳送交易的程式碼對於對於管理多個使用者的私鑰交易所和錢包很有幫助。

透過JSON RPC或者智慧合約可以一次性將收集好的簽名交易釋出到區塊鏈上。以下程式碼僅使用了Web3.js,程式碼實現建立然後批次釋出由每一個私鑰生成的委託簽名的功能。

const myWalletAddress = web3.eth.accounts.wallet[0].address;
var batch = new web3.BatchRequest();
signatures.forEach((signature) => {
  const { delegatee, nonce, expiry, v, r, s } = signature;
  batch.add(comp.methods.delegateBySig(delegatee, nonce, expiry, v, r, s).send.request(
    {
      from: myWalletAddress,
      gasLimit: web3.utils.toHex(1000000),
      gasPrice: web3.utils.toHex(25000000000),
    },
      console.log
    )
  );
});
await batch.execute();

Gas使用量: 306046

web3.BatchRequest物件將delegateBySig 簽名打包,然後釋出到區塊鏈上。完整的Node.js程式碼[25] 可以在compound治理樣例碼倉庫[26]檢視。更多關於Web.js打包可以檢視文件[27].

使用Solidity智慧合約也可以到達同樣的效果,經過對比,這種方式消耗的gas更少。

pragma solidity ^0.5.16;
pragma experimental ABIEncoderV2;
interface Comp {
  function delegateBySig(
    address delegatee,
    uint nonce,
    uint expiry,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;
}
contract BatchDelegate {
  struct Sig {
    address delegatee;
    uint nonce;
    uint expiry;
    uint8 v;
    bytes32 r;
    bytes32 s;
  }
  function delegateBySigs(Sig[] memory sigs) public {
    Comp comp = Comp(0xc00e94Cb662C3520282E6f5717214004A7f26888);
    for (uint i = 0; i < sigs.length; i++) {
      Sig memory sig = sigs[i];
      comp.delegateBySig(sig.delegatee, sig.nonce, sig.expiry, sig.v, sig.r, sig.s);
    }
  }
}

Gas使用量: 306046

完整的Solidity程式碼[28] 可以在compound治理樣例程式碼倉庫[29]找到,裡面還有JSON RPC的程式碼和智慧合約部署程式碼。

參考資料

[1]登鏈翻譯計劃: https://github.com/lbc-team/Pioneer
[2]DIFENG: https://learnblockchain.cn/people/1234
[3]Tiny熊: https://learnblockchain.cn/people/15
[4]Compound: https://learnblockchain.cn/article/1015
[5]COMP代幣: https://etherscan.io/address/0xc00e94cb662c3520282e6f5717214004a7f26888
[6]簽名: https://learnblockchain.cn/article/786
[7]gas: https://learnblockchain.cn/2019/06/11/gas-mean
[8]EIP-712: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md
[9]COMP代幣: https://etherscan.io/address/0xc00e94cb662c3520282e6f5717214004a7f26888
[10]函式: https://compound.finance/docs/governance#delegate-by-signature
[11]Compound治理提案: https://compound.finance/governance/proposals
[12]函式: https://compound.finance/docs/governance#cast-vote-by-signature
[13]此程式碼: https://github.com/compound-developers/compound-governance-examples/tree/master/signature-examples
[14]MetaMask: https://metamask.io/
[15]錢包: https://learnblockchain.cn/2019/04/11/wallet-dev-guide
[16]資料簽名: https://docs.metamask.io/guide/signing-data.html
[17]Metamask: https://learnblockchain.cn/article/1217
[18]函式: https://docs.metamask.io/guide/signing-data.html#sign-typed-data-v4
[19]結構化資料簽名: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-typed-structured-data-%F0%9D%95%8A
[20]nonce: https://learnblockchain.cn/2019/07/09/4b493e1fb352
[21]完整程式碼: https://github.com/compound-developers/compound-governance-examples/tree/master/signature-examples
[22]compound治理樣例程式碼倉庫: https://github.com/compound-developers/compound-governance-examples
[23]完整頁面程式碼: https://github.com/compound-developers/compound-governance-examples/tree/master/signature-examples
[24]compound治理樣例程式碼倉庫: https://github.com/compound-developers/compound-governance-examples
[25]完整的Node.js程式碼: https://github.com/compound-developers/compound-governance-examples/tree/master/signature-examples/batch-publish-examples
[26]compound治理樣例碼倉庫: https://github.com/compound-developers/compound-governance-examples
[27]文件: https://web3js.readthedocs.io/en/v1.2.9/web3.html#batchrequest
[28]Solidity程式碼: https://github.com/compound-developers/compound-governance-examples/tree/master/signature-examples/batch-publish-examples
[29]compound治理樣例程式碼倉庫: https://github.com/compound-developers/compound-governance-examples
[30]Compound 新聞: https://compound.substack.com/?utm_source=guide_signatures
[31]Discord: https://compound.finance/discord
[32]Adam Bavosa: /@adam.bavosa?source=post_page-----a636c9dfec5e----------------------

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;