使用此程式碼[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----------------------