在ethers中Signer是以太坊账户的抽象,可以用来签名消息和交易,如将签名的交易发送到以太坊网络以执行状态更改的操作。
可用的操作里在很大程度上取决于所使用的子类。
例如,一个来自MetaMask的Signer可以发送交易和签名消息,但不能签名一个不广播的交易。
你会遇到的最常见的Signers有:
Signer类是抽象的,不能直接实例化,而是应该使用一个具体的子类,如 Wallet, VoidSigner 或 JsonRpcSigner。
signer.connect( provider ) ⇒ Signer 子类必须实现这个,但是如果更改后的providers是不被支持的话,它们可能仅仅只抛出一个错误。
signer.getAddress( ) ⇒ Promise< string< 地址(Address) > > 返回一个解析为帐户地址的Promise。
这是一个Promise,因此一个Signer可以围绕一个异步源进行设计,如硬钱包。
子类必须实现这个。
Signer.isSigner( object ) ⇒ boolean 当且仅当 object 是一个Signer时返回true。
signer.getBalance( [ blockTag = "
latest"
] ) ⇒ Promise< 大数(BigNumber) > signer.getChainId( ) ⇒ Promise< number > signer.getTransactionCount( [ blockTag = "
latest"
] ) ⇒ Promise< number > 返回此帐户曾经发送的交易数量,交易中的中nonce依赖这个值。
signer.call( transactionRequest ) ⇒ Promise< string< DataHexString > > 返回transactionRequest调用的结果,此帐户地址用作from
字段。
signer.estimateGas( transactionRequest ) ⇒ Promise< 大数(BigNumber) > 返回发送transactionRequest的估算费用,在使用这个方法之前先用账户地址填充from
字段。
signer.resolveName( ensName ) ⇒ Promise< string< 地址(Address) > >
signer.signMessage( message ) ⇒ Promise< string< RawSignature > > 这将返回一个解析为消息的Raw SignaturePromise。
由于使用的是hashMessage方法,因此它是EIP-191兼容的。 如果在Solidity中恢复地址,则需要这个前缀来创建一个匹配的哈希。
子类必须实现这个方法。 如果不支持签名消息可能会抛出错误,比如在基于合约的钱包或基于元交易的钱包中使用时。
Note
如果message是一个字符串,它将被视为一个字符串并转换为UTF8字节的表示形式。
当且仅当消息是Bytes时,它将被视为二进制数据。
例如,字符串"0x1234"
是6个字符长(在本例中是6字节长)。 这与数组[ 0x12, 0x34 ]
不同,数组长度为2字节。
一种常见的情况是对哈希签名。在本例中,如果哈希是一个字符串,则必须首先使用arrayifyutility函数将其转换为数组。
signer.signTransaction( transactionRequest ) ⇒ Promise< string< DataHexString > > 返回一个解析为transactionRequest中已签名的交易的Promise。 此方法不填充任何缺少的字段。
子类必须实现这个,如果不支持签名消息可能会抛出错误,出于安全这在许多客户端中是常见的。
signer._signTypedData( domain , types , value ) ⇒ Promise< string< RawSignature > > <<<<<<< Updated upstream 签名使用EIP-712规范,在作用域中采用类型数据结构作为类型化的数据值。 ======= 使用EIP-712规范,采用类型数据结构作为领域签署类型化的数据值。 >>>>>>> Stashed changes
Experimental feature (方法名称会做更改)
这仍然是一个实验中的功能。如果使用它,请指定您正在使用的ethers的确切版本(例如指定"5.0.18"
,而不是"^5.0.18"
), 因为方法名将从_signTypedData
重命名为signTypedData
。
const domain = {
name: 'Ether Mail',
version: '1',
chainId: 1,
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC'
};
const types = {
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallet', type: 'address' }
],
Mail: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person' },
{ name: 'contents', type: 'string' }
]
};
const value = {
from: {
name: 'Cow',
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826'
},
to: {
name: 'Bob',
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB'
},
contents: 'Hello, Bob!'
};
signature = await signer._signTypedData(domain, types, value);
// '0x463b9c9971d1a144507d2e905f4e98becd159139421a4bb8d3c9c2ed04eb401057dd0698d504fd6ca48829a3c8a7a98c1c961eae617096cb54264bbdd082e13d1c'
Signer的所有重要属性都是不可变的,这一点非常重要。由于以太坊是异步的,并处理关键数据(如ether和其他潜在有价值的加密资产), 整个Signer的生命周期中保持provider和 address等属性是静态的有助于防止严重的问题的出现, 而且许多其他类和库也是认定provider和 address等属性是静态的。
子类必须扩展Signer,并且必须调用super()
。
这通常不需要重写,但可能需要在子类中提供自定义操作。
这应该返回一个transactionRequest的副本,包含call
、estimateGas
和populateTransaction
(sendTransaction使用的)所需的任何属性。 如果指定了任何未知的key,它会抛出一个错误。
默认的实现只验证有效的TransactionRequest属性是否存在,如果不存在,则将from
添加到交易中。
如果存在from
字段,则必须验证它与 Signer的地址是否相等。
这通常不需要重写,但可能需要在子类中提供自定义操作。
这应该返回一个transactionRequest的副本,遵循与checkTransaction
相同的过程, 并填写发送交易所需的任何属性。返回的结果都是promises,可以使用resolvePropertiesutility函数来解析。
默认实现调用checkTransaction
,如果它是一个ENS name就会解析它,并根据Signer上的相关操作添加gasPrice
, nonce
, gasLimit
和chainId
。
Wallet类继承了Signer,可以使用私钥作为外部拥有帐户(EOA)的标准对交易和消息进行签名。
new ethers.Wallet( privateKey [ , provider ] ) 为privateKey创建一个新的钱包实例,并可选地连接到provider。
ethers.Wallet.createRandom( [ options = {}
] ) ⇒ Wallet 返回一个带有随机私钥的新钱包,由加密安全的熵源生成。如果当前环境没有安全的熵源,则会抛出错误。
使用此方法创建的钱包将具有助记词。
ethers.Wallet.fromEncryptedJson( json , password [ , progress ] ) ⇒ Promise< Wallet > 从加密的JSON钱包创建一个实例。
如果提供了进度,它将在解密期间被调用,其值介于0到1之间,表示一个完成进度。
ethers.Wallet.fromEncryptedJsonSync( json , password ) ⇒ Wallet 从加密的JSON钱包创建一个实例。
此操作将同步操作,从而锁定用户界面一段时间。 大多数应用程序应该使用异步的fromEncryptedJson
。
ethers.Wallet.fromMnemonic( mnemonic [ , path , [ wordlist ] ] ) ⇒ Wallet 从助记短语中创建实例。
如果没有指定path,则使用以太坊的默认path路径(如m/44'/60'/0'/0/0
).
如果不指定wordlist,则使用English Wordlist。
注意
一个钱包实例是不可变的,因此如果您希望更改Provider,您可以使用connect方法创建一个连接到所需的provider的新实例。
wallet.encrypt( password , [ options = {}
, [ progress ] ] ) ⇒ Promise< string > 加密钱包,使用password返回一个解析为JSON钱包的Promise。
如果提供了进度,它将在解密期间被调用,其值介于0到1之间,表示一个完成进度。
mnemonic = "announce room limb pattern dry unit scale effort smooth jazz weasel alcohol"
walletMnemonic = Wallet.fromMnemonic(mnemonic)
walletPrivateKey = new Wallet(walletMnemonic.privateKey)
walletMnemonic.address === walletPrivateKey.address
// true
await walletMnemonic.getAddress()
// '0x71CB05EE1b1F506fF321Da3dac38f25c0c9ce6E1'
walletMnemonic.address
// '0x71CB05EE1b1F506fF321Da3dac38f25c0c9ce6E1'
walletMnemonic.privateKey
// '0x1da6847600b0ee25e9ad9a52abbd786dd2502fa4005dd5af9310b7cc7a3b25db'
walletMnemonic.publicKey
// '0x04b9e72dfd423bcf95b3801ac93f4392be5ff22143f9980eb78b3a860c4843bfd04829ae61cdba4b3b1978ac5fc64f5cc2f4350e35a108a9c9a92a81200a60cd64'
walletMnemonic.mnemonic
// {
// locale: 'en',
// path: "m/44'/60'/0'/0/0",
// phrase: 'announce room limb pattern dry unit scale effort smooth jazz weasel alcohol'
// }
walletPrivateKey.mnemonic
// null
await walletMnemonic.signMessage("Hello World")
// '0x14280e5885a19f60e536de50097e96e3738c7acae4e9e62d67272d794b8127d31c03d9cd59781d4ee31fb4e1b893bd9b020ec67dfa65cfb51e2bdadbb1de26d91c'
tx = {
to: "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
value: utils.parseEther("1.0")
}
await walletMnemonic.signTransaction(tx)
// '0xf865808080948ba1f109551bd432803012645ac136ddd64dba72880de0b6b3a7640000801ca0918e294306d177ab7bd664f5e141436563854ebe0a3e523b9690b4922bbb52b8a01181612cec9c431c4257a79b8c9f0c980a2c49bb5a0e6ac52949163eeb565dfc'
wallet = walletMnemonic.connect(provider)
await wallet.getBalance();
// { BigNumber: "6846" }
await wallet.getTransactionCount();
// 3
await wallet.sendTransaction(tx)
// {
// accessList: [],
// chainId: 31337,
// confirmations: 0,
// data: '0x',
// from: '0x894ed91B666FacCe5a4D2FF8261924b4754A5759',
// gasLimit: { BigNumber: "21001" },
// gasPrice: null,
// hash: '0x1b4a95e9d23bd96d5429c535148eb3eaed326d89118b118e44cc05c26703224e',
// maxFeePerGas: { BigNumber: "1572346688" },
// maxPriorityFeePerGas: { BigNumber: "1500000000" },
// nonce: 5,
// r: '0x5e32c2741700a9120cfa186bc74e88b3d9488393be796a5124fddf08ffdbfdc6',
// s: '0x2431f3e3274cd22d6de63ed6e23a5c6839c1fabeb97b6683fb15584b9bf1f29d',
// to: '0x8ba1f109551bD432803012645Ac136ddd64DBA72',
// type: 2,
// v: 0,
// value: { BigNumber: "1000000000000000000" },
// wait: [Function]
// }
一个VoidSigner是一个简单的Signer,它不能签名。
当API需要signer作为参数时,它作为只读的signer是有用的,但它只能携带只读的操作。
比如,在call
函数调用期间会自动传递所提供的地址。
new ethers.VoidSigner( address [ , provider ] ) ⇒ VoidSigner VoidSigner Pre-flight Example
address = "0x8ba1f109551bD432803012645Ac136ddd64DBA72"
signer = new ethers.VoidSigner(address, provider)
abi = [
"function balanceOf(address) view returns (uint)",
"function transfer(address, uint) returns (bool)"
]
contract = new ethers.Contract("dai.tokens.ethers.eth", abi, signer)
tokens = await contract.balanceOf(signer.getAddress())
// { BigNumber: "2413468059122458201631" }
await contract.callStatic.transfer("donations.ethers.eth", tokens)
// true
await contract.callStatic.transfer("donations.ethers.eth", tokens.add(1))
// [Error: call revert exception; VM Exception while processing transaction: reverted with reason string "Dai/insufficient-balance" [ See: https://links.ethers.org/v5-errors-CALL_EXCEPTION ]] {
// address: 'dai.tokens.ethers.eth',
// args: [
// 'donations.ethers.eth',
// { BigNumber: "2413468059122458201632" }
// ],
// code: 'CALL_EXCEPTION',
// data: '0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000184461692f696e73756666696369656e742d62616c616e63650000000000000000',
// errorArgs: [
// 'Dai/insufficient-balance'
// ],
// errorName: 'Error',
// errorSignature: 'Error(string)',
// method: 'transfer(address,uint256)',
// reason: 'Dai/insufficient-balance',
// transaction: {
// data: '0xa9059cbb000000000000000000000000643aa0a61eadcc9cc202d1915d942d35d005400c000000000000000000000000000000000000000000000082d598fcab89948e20',
// from: '0x8ba1f109551bD432803012645Ac136ddd64DBA72',
// to: '0x6B175474E89094C44Da98b954EedeAC495271d0F'
// }
// }
这个接口包含外部拥有帐户(EOA)所需的最小属性集,可以执行某些操作,比如将其编码为JSON钱包。
可选的。帐户HD的助记词,如果有的话可以打印出来。EOA账户源不编码助记符,如HD extended keys。