Sign¶
普通签名¶
参考:https://github.com/WTFAcademy/WTF-Ethers/tree/main/18_Signature
const provider = new ethers.JsonRpcProvider(Infura_URL);
const privateKey = '0x227dbb8586...75d593b6f2b';
const wallet = new ethers.Wallet(privateKey, provider);
// 交易信息经过Keccak256哈希,获得消息摘要
// 等效于Solidity中的keccak256(abi.encodePacked(account, tokenId))
const account = "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"
const tokenId = "0"
const msgHash = ethers.solidityPackedKeccak256(
['address', 'uint256'],
[account, tokenId]
)
console.log(`msgHash:${msgHash}`)
// 使用钱包对消息摘要签名
// signMessage()函数进行符合EIP191标准的签名:避免用户误签恶意交易,在消息前加上"\x19Ethereum Signed Message:\n32"字符,再做一次keccak256哈希得到以太坊签名消息,然后再签名
const messageHashBytes = ethers.getBytes(msgHash)
const signature = await wallet.signMessage(messageHashBytes);
console.log(`签名:${signature}`)
EIP712 标准签名¶
参考:https://github.com/WTFAcademy/WTF-Ethers/tree/main/26_EIP712
EIP712 提供了一种更高级、更安全的签名方法。
当支持 EIP712 的 Dapp 请求签名时,钱包会展示签名消息的原始数据,用户可以在验证数据符合预期之后签名
- 创建 EIP712 Domain
let contractName = "EIP712Storage" // 合约名
let version = "1" // 版本,通常约定为1
let chainId = "1"
let contractAddress = "0xf8e81D47203A594245E36C48e151709F0C19fBe8" // 验证签名的合约地址
const domain = {
name: contractName,
version: version,
chainId: chainId,
verifyingContract: contractAddress,
};
- 创建类型化数据,Storage
let spender = "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"
let number = "100"
// 声明类型
const types = {
Storage: [
{ name: "spender", type: "address" },
{ name: "number", type: "uint256" },
],
};
// 包含数据
const message = {
spender: spender,
number: number,
};
- 调用 signTypedData() 签名
const signature = await wallet.signTypedData(domain, types, message);
`
0xdca07f0c1dc70a4f9746a7b4be145c3bb8c8503368e94e3523ea2e8da6eba7b61f260887524f015c82dd77ebd3c8938831c60836f905098bf71b3e6a4a09b7311b
`
- 合约侧验证签名
// 验证 EIP712 签名,从签名和消息复原出 signer 地址
let eip712Signer = ethers.verifyTypedData(domain, types, message, signature)
`
0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
`
连接 MetaMask¶
参考:https://github.com/WTFAcademy/WTF-Ethers/tree/main/ET01_Metamask
首先需要安装 MetaMask 浏览器插件,一定要到官网下载:https://metamask.io/download/
安装后浏览器会给每个页面注入一个 window.ethereum
对象,用于和钱包交互
Ether.js 提供了 BrowserProvider
对象封装了一个标准的 Web3 Provider 与其交互
const provider = new ethers.BrowserProvider(window.ethereum)
// 读取钱包地址
const accounts = await provider.send("eth_requestAccounts", []);
// 获取ChainID
const { chainId } = await provider.getNetwork()
// 读取钱包ETH余额
const signer = await provider.getSigner()
const balance = await provider.getBalance(signer.getAddress());
使用 Metamask 签名授权登陆¶
参考:https://github.com/WTFAcademy/WTF-Ethers/tree/main/ET02_SignInWithEthereum
多签¶
为了避免一个私钥的丢失导致地址的资金丢失,引出了多重签名机制,可以实现分散风险的功能。
假设N个人分别持有N个私钥,需要要其中M个人同意签名才可以动用某个“联合地址”的资金
最常见的多重签名是2-3类型。例如,一个提供在线钱包的服务,为了防止服务商盗取用户的资金,可以使用2-3类型的多重签名地址,服务商持有1个私钥,用户持有两个私钥,一个作为常规使用,一个作为应急使用。这样,正常情况下,用户只需使用常规私钥即可配合服务商完成正常交易,服务商因为只持有1个私钥,因此无法盗取用户资金。如果服务商倒闭或者被黑客攻击,用户可使用自己掌握的两个私钥转移资金。