Contracts
A Contract is an abstraction of an executable program on the Ethereum Blockchain. A Contract has code (called byte code) as well as allocated long-term memory (called storage). Every deployed Contract has an address, which is used to connect to it so that it may be sent messages to call its methods.
合约是Ethereum区块链上可执行程序的抽象。合约有代码(称为字节码)和分配的长期内存(称为存储)。每个部署的合约都有一个地址,用来连接到它,以便可以发送消息来调用它的方法。
A Contract can emit Events, which can be efficiently observed by applications to be notified when a contract has performed specific operation. Events cannot be read by a Contract.
合约可以发出事件,应用程序可以有效地观察这些事件,以便在合约执行特定操作时通知它们。事件不能被合约读取。
There are two types of methods that can be called on a Contact:两种类型的方法
A Constant method may not add, remove or change any data in the storage, nor log any events, and may only call Constant methods on other contracts. These methods are free (noEther is required) to call. The result from them may also be returned to the caller.
常量方法不能添加、删除或更改存储中的任何数据,也不能记录任何事件,只能在其他合约上调用常量方法。这些方法可以自由调用(不需要ether)。它们的结果也可以返回给调用者。
A Non-Constant method requires a fee (in Ether) to be paid, but may perform any state-changing operation desired, log events, send ether and call Non-Constant methods on other Contracts. These methods cannot return their result to the caller. These methods must be triggered by a transaction, sent by an Externally Owned Account (EOA) either directly or indirectly (i.e. called from another contract), and are required to be mined before the effects are present. Therefore, the duration required for these operations can vary widely, and depend on the transaction gas price, network congestion and miner priority heuristics.
非常量方法需要支付一定的费用(以Ether表示),但可以执行任何想要的状态更改操作、记录事件、发送Ether和调用其他合约上的非常量方法。这些方法无法将结果返回给调用方。这些方法必须由交易触发,由外部拥有的帐户(EOA)直接或间接发送(即从另一个契约调用),并且需要在产生效果之前进行挖掘。因此,这些操作所需的持续时间可能相差很大,并且取决于交易gas价格、网络拥塞和矿工优先级。
The Contract API provides simple way to connect to a Contract and call its methods, as functions on a JavaScript object, handling all the binary protocol conversion, internal name mangling and topic construction. This allows a Contract object to be used like any standard JavaScript object, without having to worry about the low-level details of the Ethereum Virtual Machine or Blockchain.
合约API提供了连接到合约并调用其方法的简单方法,有作为JavaScript对象上的函数,处理所有二进制协议转换、内部名称混乱和主题构造。这允许合约对象像任何标准JavaScript对象一样使用,而不必担心Ethereum虚拟机或区块链的底层细节。
The Contract object is a meta-class, which is a class that defines a Class at run-time. The Contract definition (called an Application Binary Interface, or ABI) can be provided and the available methods and events will be dynamically added to the object.
合约对象是一个元类,它是在运行时定义类的类。可以提供合约定义(称为应用程序二进制接口,或ABI),并将可用的方法和事件动态添加到对象中。
Throughout this document, we will refer to the following Contract.
在本文件中,我们将参考以下合约:
SimpleStorage Contract
pragma solidity ^0.4.24; contract SimpleStorage { event ValueChanged(address indexed author, string oldValue, string newValue); string _value; constructor(string value) public { emit ValueChanged(msg.sender, _value, value); _value = value; } function getValue() view public returns (string) { return _value; } function setValue(string value) public { emit ValueChanged(msg.sender, _value, value); _value = value; } }
Deploying a Contract部署合约
To deploy a contract to the Ethereum network, a ContractFactory can be created which manages the Contract bytecode and Application Binary Interface (ABI), usually generated from the Solidity compiler.
要将合约部署到Ethereum网络,可以创建一个ContractFactory来管理合约字节码和应用程序二进制接口(ABI),通常由Solidity编译器(如remix)生成。
Creating a Contract Factory创建Contract Factory的三种方法
- new ethers . ContractFactory ( abi , bytecode [ , signer ] )
- Creates a factory for deployment of the Contract with bytecode, and the constructor defined in the abi. The signer will be used to send any deployment transaction.创建一个工厂,用于部署带有字节码的合约和abi中定义的构造函数。签名者将用于发送任何部署交易。
- ethers . ContractFactory . fromSolidity ( compilerOutput [ , signer ] )
- Creates a ContractFactory from the compilerOutput of the Solidity compiler or from the Truffle JSON. 从Solidity编译器的编译器输出或Truffle JSON创建一个ContractFactory(i.e.
output.contracts['SimpleStorage.sol'].SimpleStorage
) - prototype . connect ( signer ) => ContractFactory
- Create a new instance of the ContractFactory, connected to the new signer.创建一个新的ContractFactory实例,连接到新的签名者
Prototype
- prototype . bytecode
- The Contract executable byte code..
- prototype . interface
- The Contract Application Binary Interface (ABI).
- prototype . signer
- The Signer that will be used to send transactions to the network. If this is null,
deploy()
cannot be called. signer将用于向网络发送交易。如果为null,则无法调用deploy()
Connecting
- prototype . attach ( address ) => Contract
- Connect to an existing instance of this Contract at address using the Contract Interface and Signer.连接一个已经存在的合约实例address
Deployment
- prototype . deploy ( … ) => Promise<Contract>
-
Creates a transaction to deploy the transaction and sends it to the network using the contract Signer, returning a Promise that resolves to a Contract. The transaction is available as contract.deployTransaction.
创建用于部署交易的交易,并使用交易签名者将其发送到网络,返回解析返回合约的Promise。该交易可以作为contract.deployTransaction使用。
Keep in mind that the Contract may not be mined immediately. The
contract.deployed()
function will return a Promise which will resolve once the contract is deployed, or reject if there was an error during deployment.请记住,合同不能立即解除。deploy()函数将返回一个Promise,该Promise将在部署合约时解析,或者在部署过程中出现错误时拒绝。
- prototype . getDeployTransaction ( … ) => UnsignedTransaction
- Returns the transaction required to deploy the Contract with the provided constructor arguments. This is often useful for signing offline transactions or analysis tools.返回使用提供的构造函数参数部署合约所需的交易。这对于签名脱机交易或分析工具通常很有用。
Waiting for Deployment等待部署到节点中
- prototype . deployed ( ) => Promise<Contract>
- If the contract is the result of
deploy()
, returns a Promise that resolves to the contract once it has been mined, or rejects if the contract failed to deploy. If the contract has been deployed already, this will return a Promise that resolves once the on-chain code has been confirmed.
const ethers = require('ethers'); var test = async () =>{ // The Contract interface let abi = [ "event ValueChanged(address indexed author, string oldValue, string newValue)", "constructor(string value)", "function getValue() view returns (string value)", "function setValue(string value)" ]; // The bytecode from Solidity, compiling the above source let bytecode = "0x608060405234801561001057600080fd5b506040516105bd3803806105bd8339" + "8101604081815282518183526000805460026000196101006001841615020190" + "91160492840183905293019233927fe826f71647b8486f2bae59832124c70792" + "fba044036720a54ec8dacdd5df4fcb9285919081906020820190606083019086" + "9080156100cd5780601f106100a2576101008083540402835291602001916100" + "cd565b820191906000526020600020905b815481529060010190602001808311" + "6100b057829003601f168201915b505083810382528451815284516020918201" + "9186019080838360005b838110156101015781810151838201526020016100e9" + "565b50505050905090810190601f16801561012e578082038051600183602003" + "6101000a031916815260200191505b5094505050505060405180910390a28051" + "610150906000906020840190610157565b50506101f2565b8280546001816001" + "16156101000203166002900490600052602060002090601f0160209004810192" + "82601f1061019857805160ff19168380011785556101c5565b82800160010185" + "5582156101c5579182015b828111156101c55782518255916020019190600101" + "906101aa565b506101d19291506101d5565b5090565b6101ef91905b80821115" + "6101d157600081556001016101db565b90565b6103bc806102016000396000f3" + "0060806040526004361061004b5763ffffffff7c010000000000000000000000" + "0000000000000000000000000000000000600035041663209652558114610050" + "57806393a09352146100da575b600080fd5b34801561005c57600080fd5b5061" + "0065610135565b60408051602080825283518183015283519192839290830191" + "85019080838360005b8381101561009f57818101518382015260200161008756" + "5b50505050905090810190601f1680156100cc57808203805160018360200361" + "01000a031916815260200191505b509250505060405180910390f35b34801561" + "00e657600080fd5b506040805160206004803580820135601f81018490048402" + "8501840190955284845261013394369492936024939284019190819084018382" + "80828437509497506101cc9650505050505050565b005b600080546040805160" + "20601f6002600019610100600188161502019095169490940493840181900481" + "0282018101909252828152606093909290918301828280156101c15780601f10" + "610196576101008083540402835291602001916101c1565b8201919060005260" + "20600020905b8154815290600101906020018083116101a457829003601f1682" + "01915b505050505090505b90565b604080518181526000805460026000196101" + "00600184161502019091160492820183905233927fe826f71647b8486f2bae59" + "832124c70792fba044036720a54ec8dacdd5df4fcb9285918190602082019060" + "60830190869080156102715780601f1061024657610100808354040283529160" + "200191610271565b820191906000526020600020905b81548152906001019060" + "200180831161025457829003601f168201915b50508381038252845181528451" + "60209182019186019080838360005b838110156102a557818101518382015260" + "200161028d565b50505050905090810190601f1680156102d257808203805160" + "01836020036101000a031916815260200191505b509450505050506040518091" + "0390a280516102f49060009060208401906102f8565b5050565b828054600181" + "600116156101000203166002900490600052602060002090601f016020900481" + "019282601f1061033957805160ff1916838001178555610366565b8280016001" + "0185558215610366579182015b82811115610366578251825591602001919060" + "01019061034b565b50610372929150610376565b5090565b6101c991905b8082" + "1115610372576000815560010161037c5600a165627a7a723058202225a35c50" + "7b31ac6df494f4be31057c7202b5084c592bdb9b29f232407abeac0029"; // Connect to the network // let provider = ethers.getDefaultProvider('ropsten'); let provider = new ethers.providers.JsonRpcProvider("http://localhost:8545"); // Load the wallet to deploy the contract with let privateKey = '0x9af0f29100b9575ffe7d5596d87003b6ada89076ca84342f1fe57d85acde4ca6'; let wallet = new ethers.Wallet(privateKey, provider);//做signer // Create an instance of a Contract Factory let factory = new ethers.ContractFactory(abi, bytecode, wallet); // Notice we pass in "Hello World" as the parameter to the constructor let contract = await factory.deploy("Hello World");//"Hello World"为该合约构造函数所需的参数 // The address the Contract WILL have once mined console.log(contract); console.log(contract.address);//0xB6337288733B16AaB7691ecB2Aa554396bab242E // The transaction that was sent to the network to deploy the Contract console.log(contract.deployTransaction.hash);//0x5d409fac068271db50ee4deecf9310a5ac31862435477b56deea74484269d010 // The contract is NOT deployed yet; we must wait until it is mined await contract.deployed()//上面是部署的设置,这里则是开始部署到节点上 // Done! The contract is deployed. } test();
console.log(contract);返回:
Contract { interface: Interface { functions: { getValue: _FunctionDescription { inputs: [], outputs: [ { type: 'string', name: 'value' } ], gas: null, payable: false, type: 'call', signature: 'getValue()', sighash: '0x20965255' }, 'getValue()': _FunctionDescription { inputs: [], outputs: [ { type: 'string', name: 'value' } ], gas: null, payable: false, type: 'call', signature: 'getValue()', sighash: '0x20965255' }, setValue: _FunctionDescription { inputs: [ { type: 'string', name: 'value' } ], outputs: [], gas: null, payable: false, type: 'transaction', signature: 'setValue(string)', sighash: '0x93a09352' }, 'setValue(string)': _FunctionDescription { inputs: [ { type: 'string', name: 'value' } ], outputs: [], gas: null, payable: false, type: 'transaction', signature: 'setValue(string)', sighash: '0x93a09352' } }, events: { ValueChanged: _EventDescription { name: 'ValueChanged', signature: 'ValueChanged(address,string,string)', inputs: [ { type: 'address', name: 'author', indexed: true }, { type: 'string', name: 'oldValue', indexed: false }, { type: 'string', name: 'newValue', indexed: false } ], topic: '0xe826f71647b8486f2bae59832124c70792fba044036720a54ec8dacdd5df4fcb', anonymous: false }, 'ValueChanged(address,string,string)': _EventDescription { name: 'ValueChanged', signature: 'ValueChanged(address,string,string)', inputs: [ { type: 'address', name: 'author', indexed: true }, { type: 'string', name: 'oldValue', indexed: false }, { type: 'string', name: 'newValue', indexed: false } ], topic: '0xe826f71647b8486f2bae59832124c70792fba044036720a54ec8dacdd5df4fcb', anonymous: false } }, abi: [ { anonymous: false, inputs: [ { type: 'address', name: 'author', indexed: true }, { type: 'string', name: 'oldValue', indexed: false }, { type: 'string', name: 'newValue', indexed: false } ], name: 'ValueChanged', type: 'event' }, { constant: false, gas: null, inputs: [ { type: 'string', name: 'value' } ], payable: false, stateMutability: null, type: 'constructor' }, { constant: true, gas: null, inputs: [], name: 'getValue', outputs: [ { type: 'string', name: 'value' } ], payable: false, stateMutability: 'view', type: 'function' }, { constant: false, gas: null, inputs: [ { type: 'string', name: 'value' } ], name: 'setValue', outputs: [], payable: false, stateMutability: null, type: 'function' } ], deployFunction: _DeployDescription { inputs: [ { type: 'string', name: 'value' } ], payable: false } }, provider: JsonRpcProvider { ready: Promise { { chainId: 1543212825264, name: 'unknown' } }, _lastBlockNumber: -2, _balances: {}, _events: [], _pollingInterval: 4000, _emitted: { block: -2 }, _fastQueryDate: 0, connection: { url: 'http://localhost:8545' }, _network: { chainId: 1543212825264, name: 'unknown' } }, signer: Wallet { signingKey: SigningKey { privateKey: '0x9af0f29100b9575ffe7d5596d87003b6ada89076ca84342f1fe57d85acde4ca6', keyPair: KeyPair { privateKey: '0x9af0f29100b9575ffe7d5596d87003b6ada89076ca84342f1fe57d85acde4ca6', publicKey: '0x04e5a5bca0b02b9b96b9009023adf1ef37bc79e4962b6ea11e1429cc03dc0a41ae3520322e2d5bfddc175a0c0883478bd372fee2b81dd28629fa1f8e10f390bfad', compressedPublicKey: '0x03e5a5bca0b02b9b96b9009023adf1ef37bc79e4962b6ea11e1429cc03dc0a41ae', publicKeyBytes: [ 3, 229, 165, 188, 160, 176, 43, 155, 150, 185, 0, 144, 35, 173, 241, 239, 55, 188, 121, 228, 150, 43, 110, 161, 30, 20, 41, 204, 3, 220, 10, 65, 174 ] }, publicKey: '0x04e5a5bca0b02b9b96b9009023adf1ef37bc79e4962b6ea11e1429cc03dc0a41ae3520322e2d5bfddc175a0c0883478bd372fee2b81dd28629fa1f8e10f390bfad', address: '0x3455f15cc11F2E77c055f931A6C918ccc7c18fd8' }, provider: JsonRpcProvider { ready: Promise { { chainId: 1543212825264, name: 'unknown' } }, _lastBlockNumber: -2, _balances: {}, _events: [], _pollingInterval: 4000, _emitted: { block: -2 }, _fastQueryDate: 0, connection: { url: 'http://localhost:8545' }, _network: { chainId: 1543212825264, name: 'unknown' } } }, estimate: { getValue: [Function], 'getValue()': [Function], setValue: [Function], 'setValue(string)': [Function] }, functions: { getValue: [Function], 'getValue()': [Function], setValue: [Function], 'setValue(string)': [Function] }, filters: { ValueChanged: [Function], 'ValueChanged(address,string,string)': [Function] }, _events: [], address: '0xB6337288733B16AaB7691ecB2Aa554396bab242E',//合约地址 addressPromise: Promise { '0xB6337288733B16AaB7691ecB2Aa554396bab242E' }, getValue: [Function], 'getValue()': [Function], setValue: [Function], 'setValue(string)': [Function], deployTransaction://这就是contract.deployTransaction返回的内容 { nonce: 2, gasPrice: BigNumber { _hex: '0x04a817c800' }, gasLimit: BigNumber { _hex: '0x058c84' }, to: null, value: BigNumber { _hex: '0x00' }, data: '0x608060405234801561001057600080fd...b48656c6c6f20576f726c64000000000000000000000000000000000000000000', chainId: 1543212825264, v: 3086425650563, r: '0x06bf6f803fa3e90152b464ea22949b88fad6c6704015863970eb3b434040326d', s: '0x4beb385638975bc7ddd670dbe09e93b00bfa61fd2bac78c09e6621b571ba9127', from: '0x3455f15cc11F2E77c055f931A6C918ccc7c18fd8', hash: '0x5d409fac068271db50ee4deecf9310a5ac31862435477b56deea74484269d010',//该交易hash wait: [Function] } }
Connecting to Existing Contracts连接已经部署好的合约
Once a Contract has been deployed, it can be connected to using the Contract object.
一旦合约已经部署好了,就可以使用Contract对象进行连接
Connecting to a Contract
- new ethers . Contract ( addressOrName , abi , providerOrSigner )
-
Connects to the contract at addressOrName defined by abi, connected as providerOrSigner.
For supported formats for abi, see Contract ABI.
For access capabilities and restrictions, see Providers vs Signers
const ethers = require('ethers'); var test = async() =>{ // The Contract interface let abi = [ "event ValueChanged(address indexed author, string oldValue, string newValue)", "constructor(string value)", "function getValue() view returns (string value)", "function setValue(string value)" ]; // Connect to the network let provider = new ethers.providers.JsonRpcProvider("http://localhost:8545"); // The address from the above deployment example let contractAddress = "0xB6337288733B16AaB7691ecB2Aa554396bab242E"; // We connect to the Contract using a Provider, so we will only // have read-only access to the Contract,使用provider连接合约,对合约只有读的权限 let contract = new ethers.Contract(contractAddress, abi, provider); // Get the current value let currentValue = await contract.getValue(); console.log(currentValue); // "Hello World" // Set a new Value, which returns the transaction // 因为只有读的权限,所以运行调用下面的函数时会出错: //(node:4656) UnhandledPromiseRejectionWarning: Error: sending a transaction require a signer (operation="sendTransaction", version=4.0.13) let tx = await contractWithSigner.setValue("I like turtles."); console.log(tx.hash); // The operation is NOT complete yet; we must wait until it is mined await tx.wait(); } test();
⚠️:使用wallet连接合约,对合约有读写的权限-new ethers.Contract(contractAddress, abi, wallet),wallet是连接了provider的
如果wallet没有连接provider,也可以写成下面的形式:
let contract = new ethers.Contract(contractAddress, abi, provider); let contractWithSigner = contract.connect(wallet);
举例:
const ethers = require('ethers'); var test = async() =>{ // The Contract interface let abi = [ "event ValueChanged(address indexed author, string oldValue, string newValue)", "constructor(string value)", "function getValue() view returns (string value)", "function setValue(string value)" ]; // Connect to the network let provider = new ethers.providers.JsonRpcProvider("http://localhost:8545"); // A Signer from a private key let privateKey = '0x9af0f29100b9575ffe7d5596d87003b6ada89076ca84342f1fe57d85acde4ca6'; let wallet = new ethers.Wallet(privateKey, provider); // The address from the above deployment example let contractAddress = "0xB6337288733B16AaB7691ecB2Aa554396bab242E"; // We connect to the Contract using a Provider, so we will only // have read-only access to the Contract,使用wallet连接合约,对合约有写的权限;两者都使用则有读写权利 let contract = new ethers.Contract(contractAddress, abi, provider); let contractWithSigner = contract.connect(wallet);//这个就是两者都使用 //或 //let contractWithSigner = new ethers.Contract(contractAddress, abi, wallet)//这个即只使用wallet // Get the current value let currentValue = await contractWithSigner.getValue(); console.log(currentValue); // "Hello World" // Set a new Value, which returns the transaction let tx = await contractWithSigner.setValue("I like turtles."); console.log(tx.hash);//0xacfffe1d30e2821325a72efc448516d8b26a387cf611030fcab69553c94bee53 // The operation is NOT complete yet; we must wait until it is mined await tx.wait(); } test();
Providers vs Signers
A Contract object has a notion of an “frame of reference”, which will determine what type of access and whom the Contract is enacted upon as. This is specified by the providerOrSignerparameter when connecting to a Contract.
合约对象有一个“参考框架”的概念,它将决定访问的类型以及合约是根据谁制定的。这是在连接到合约时由providerOrSigner参数指定的。
There are three possible cases for connecting a Contract using the providerOrSigner.
providerOrSigner | Operation Privileges |
---|---|
Provider | Read-Only Access |
Signer (without a provider) | Write-Only Access (as account owner) |
Signer (with a provider) | Read and Write Access (as account owner) |
The providerOrSigner is immutable, so to change the “frame of reference” to another account or provider, use the connect
function.
⚠️providerOrSigner是不可变的,因此要将“引用框架”更改为另一个帐户或提供者,请使用connect函数
- prototype . connect ( providerOrSigner )
- Create a new instance of the Contract object connected as providerOrSigner.
添加上事件监听:
const ethers = require('ethers'); var test = async() =>{ // The Contract interface let abi = [ "event ValueChanged(address indexed author, string oldValue, string newValue)", "constructor(string value)", "function getValue() view returns (string value)", "function setValue(string value)" ]; // Connect to the network let provider = new ethers.providers.JsonRpcProvider("http://localhost:8545"); // A Signer from a private key let privateKey = '0x9af0f29100b9575ffe7d5596d87003b6ada89076ca84342f1fe57d85acde4ca6'; let wallet = new ethers.Wallet(privateKey, provider); // The address from the above deployment example let contractAddress = "0xB6337288733B16AaB7691ecB2Aa554396bab242E"; // We connect to the Contract using a Provider, so we will only // have read-only access to the Contract,使用wallet连接合约,对合约有读写的权限 let contract = new ethers.Contract(contractAddress, abi, provider); let contractWithSigner = contract.connect(wallet); //或 //let contractWithSigner = new ethers.Contract(contractAddress, abi, wallet) // Get the current value let currentValue = await contractWithSigner.getValue(); console.log(currentValue); // 'I like turtles.' // Set a new Value, which returns the transaction let tx = await contractWithSigner.setValue("I like monkey."); console.log(tx.hash);//0x5bd8c28bcc0904d328248b5321bc272cf85095981c3bcd0da1e68418665a7342 // The operation is NOT complete yet; we must wait until it is mined await tx.wait(); contractWithSigner.on("ValueChanged", (author, oldValue, newValue, event) => {//监听事件"ValueChanged" // Called when anyone changes the value console.log("anyone"); console.log(author); // "0x3455f15cc11F2E77c055f931A6C918ccc7c18fd8" console.log(oldValue); // "I like turtles." console.log(newValue); // "I like monkey." // See Event Emitter below for all properties on Event console.log(event.blockNumber); // 5 }); //Filtering an Events // A filter that matches my Signer as the author let filter = contractWithSigner.filters.ValueChanged(wallet.address); contractWithSigner.on(filter, (author, oldValue, newValue, event) => {//即是wallet.address账户调用了setValue函数时,触发并得到相应的事件信息 // Called ONLY when your account changes the value console.log("the one"); console.log(author); // "0x3455f15cc11F2E77c055f931A6C918ccc7c18fd8" console.log(oldValue); // "I like turtles." console.log(newValue); // "I like monkey." // See Event Emitter below for all properties on Event console.log(event.blockNumber); // 5 }); } test();
Prototype
- prototype . address
- The address (or ENS name) of the contract.
- prototype . deployTransaction合约是使用ContractFactory部署的时候就能够得到值,否则为null。下面例子得到undefined。因为它是连接已有的合约,并不是部署得到的,上面部署得到的例子中的确能够得到该值
- If the contract was deployed by a ContractFactory, this is the transaction used to deploy it, otherwise it is null.
- prototype . interface
- The Interface meta-class of the parsed ABI. Generally, this should not need to be accessed directly.
Additional properties will be added to the prototype at run-time, based on the ABI provided, see Contract Meta-Class.
const ethers = require('ethers'); var test = async() =>{ // The Contract interface let abi = [ "event ValueChanged(address indexed author, string oldValue, string newValue)", "constructor(string value)", "function getValue() view returns (string value)", "function setValue(string value)" ]; // Connect to the network let provider = new ethers.providers.JsonRpcProvider("http://localhost:8545"); // A Signer from a private key let privateKey = '0x9af0f29100b9575ffe7d5596d87003b6ada89076ca84342f1fe57d85acde4ca6'; let wallet = new ethers.Wallet(privateKey, provider); // The address from the above deployment example let contractAddress = "0xB6337288733B16AaB7691ecB2Aa554396bab242E"; let contract = new ethers.Contract(contractAddress, abi, provider); console.log(contract.address); console.log(contract.deployTransaction); console.log(contract.interface); } test();
返回:
0xB6337288733B16AaB7691ecB2Aa554396bab242E undefined Interface { functions: { getValue: _FunctionDescription { inputs: [], outputs: [ { type: 'string', name: 'value' } ], gas: null, payable: false, type: 'call', signature: 'getValue()', sighash: '0x20965255' }, 'getValue()': _FunctionDescription { inputs: [], outputs: [ { type: 'string', name: 'value' } ], gas: null, payable: false, type: 'call', signature: 'getValue()', sighash: '0x20965255' }, setValue: _FunctionDescription { inputs: [ { type: 'string', name: 'value' } ], outputs: [], gas: null, payable: false, type: 'transaction', signature: 'setValue(string)', sighash: '0x93a09352' }, 'setValue(string)': _FunctionDescription { inputs: [ { type: 'string', name: 'value' } ], outputs: [], gas: null, payable: false, type: 'transaction', signature: 'setValue(string)', sighash: '0x93a09352' } }, events: { ValueChanged: _EventDescription { name: 'ValueChanged', signature: 'ValueChanged(address,string,string)', inputs: [ { type: 'address', name: 'author', indexed: true }, { type: 'string', name: 'oldValue', indexed: false }, { type: 'string', name: 'newValue', indexed: false } ], topic: '0xe826f71647b8486f2bae59832124c70792fba044036720a54ec8dacdd5df4fcb', anonymous: false }, 'ValueChanged(address,string,string)': _EventDescription { name: 'ValueChanged', signature: 'ValueChanged(address,string,string)', inputs: [ { type: 'address', name: 'author', indexed: true }, { type: 'string', name: 'oldValue', indexed: false }, { type: 'string', name: 'newValue', indexed: false } ], topic: '0xe826f71647b8486f2bae59832124c70792fba044036720a54ec8dacdd5df4fcb', anonymous: false } }, abi: [ { anonymous: false, inputs: [ { type: 'address', name: 'author', indexed: true }, { type: 'string', name: 'oldValue', indexed: false }, { type: 'string', name: 'newValue', indexed: false } ], name: 'ValueChanged', type: 'event' }, { constant: false, gas: null, inputs: [ { type: 'string', name: 'value' } ], payable: false, stateMutability: null, type: 'constructor' }, { constant: true, gas: null, inputs: [], name: 'getValue', outputs: [ { type: 'string', name: 'value' } ], payable: false, stateMutability: 'view', type: 'function' }, { constant: false, gas: null, inputs: [ { type: 'string', name: 'value' } ], name: 'setValue', outputs: [], payable: false, stateMutability: null, type: 'function' } ], deployFunction: _DeployDescription { inputs: [ { type: 'string', name: 'value' } ], payable: false } }
Meta-Class Properties
Since a Contract is dynamic and loaded at run-time, many of the properties that will exist on a Contract are determined at run-time from the Contract ABI.
由于合约是动态的,并且是在运行时加载的,因此合约上存在的许多属性都是在运行时从合约ABI中确定的。
Contract Methods合约的方法
All functions populated from the ABI are also included on the contract object directly, for example contract.functions.getValue()
can also be called using contract.getValue()
.
两种调用方式:
1.contract.functions.functionName()
2.contract.
functionName
()
- prototype . functions . functionName
-
An object that maps each ABI function name to a function that will either call (for constant functions) or sign and send a transaction (for non-constant functions)
Calling a Constant function requires either a Provider or a Signer with a Provider.调用常量方法使用Provider或Signer都可以
Calling a Non-Constant function (i.e. sending a transaction) requires a Signer.调用非常量方法就只能使用Signer
- prototype . estimate . functionName 估计调用某个方法将会使用多少gas
- An object that maps each ABI function name to a function that will estimate the cost the provided parameters.
Contract Event Filters对事件的过滤
Filters allow for a flexible and efficient way to fetch only a subset of the events that match specific criteria. The filters
property contains a function for every Event in the ABI that computes a Filter for a given set of values. The null
matches any value.
过滤器允许以一种灵活和有效的方式只获取符合特定标准的事件子集。filters属性为ABI中的每个事件包含一个函数,该函数为给定的值集计算一个过滤器。null匹配任何值。
- prototype . filters . eventname(from,to)
- A function that generates filters that can be listened to, using the
on(eventName, ...)
function, filtered by the Event values.
运行例子在上面有,格式为:
// A filter from me to anyone let filterFromMe = contract.filters.Transfer(myAddress); // A filter from anyone to me let filterToMe = contract.filters.Transfer(null, myAddress); // A filter from me AND to me let filterFromMeToMe = contract.filters.Transfer(myAddress, myAddress); contract.on(filterFromMe, (fromAddress, toAddress, value, event) => { console.log('I sent', value); }); contract.on(filterToMe, (fromAddress, toAddress, value, event) => { console.log('I received', value); }); contract.on(filterFromMeToMe, (fromAddress, toAddress, value, event) => { console.log('Myself to me', value); });
Overrides
Every Contract method may take one additional (optional) parameter which specifies the transaction (or call) overrides.
每个合约方法都可以使用一个额外的(可选的)参数,该参数指定交易(或调用)覆盖。
非常量方法,生成交易:
// All overrides are optional let overrides = { // The maximum units of gas for the transaction to use gasLimit: 23000, // The price (in wei) per unit of gas gasPrice: utils.parseUnits('9.0', 'gwei'), // The nonce to use in the transaction nonce: 123, // The amount to send with the transaction (i.e. msg.value) value: utils.parseEther('1.0'), // The chain ID (or network ID) to use chainId: 1 }; // Solidity: function someFunction(address addr) public let tx = contract.someFunction(addr, overrides)
常量方法:
let overrides = { // The address to execute the call as from: "0x0123456789012345678901234567890123456789", // The maximum units of gas for the transaction to use gasLimit: 23000, }; // Solidity: function someFunction(address addr) public pure returns (bytes32 result) let result = contract.someFunction(addr, overrides)
Event Emitter(前面讲过,不再重复)
Each Contract supports many of the operations available from the Event Emitter API.
To listen for Events, the contract requires either a Provider or a Signer with a Provider.
Event Names
The available eventNames are:
- string – The name of an event (e.g. “TestEvent” or “TestEvent(string, uint)”)
- filter – See Contract Filters
- * – All events
Event Object
All event callbacks receive the parameters specified in the ABI as well as one additional Event Object with
所有事件回调都会接收ABI中指定的参数(如下)以及一个附加的事件对象
- blockNumber, blockHash, transactionHash – The Block and Transaction of the Log
- address – The contract address for the Log
- data – The Log data
- topics – An array of the Log topics
- args – An array of the parsed arguments for the event
- event – the name of the event (e.g. “Transfer”)
- eventSignature – the full signature of the event (e.g. “Transfer(address,address,uint256)”)
- getBlock() – A function that resolves to the Block containing the Log
- getTransaction() – A function that resolves to the Transaction containing the Log
- getTransactionReceipt() – A function that resolves to the Transaction Receipt containing the Log
- removeListener() – A function that removes this callack as a listener
- decode(data, topics) – A function that decodes data and topics into parsed arguments
Configuring Events
- prototype . on ( eventName , callback ) => Contract
- Registers callback to be called on every eventName. Returns the contract, so calls may be chained.
- prototype . addEventListner ( eventName , callback ) => Contract
- An alias for
on
. - prototype . once ( eventName , callback ) => Contract
- Register callback to be called at most once, for eventName. Returns the contract, so calls may be chained.
- prototype . emit ( eventName , … ) => boolean
- Trigger all callbacks for eventName, returning true if there was at least one listener. This should generally not be called directly.
- prototype . listenerCount ( [ eventName ] ) => number
- Returns the number of callbacks registered for eventName.
- prototype . listeners ( eventName ) => Listeners[]
- Returns a list of callbacks for eventName.
- prototype . removeAllListeners ( eventName ) => Contract
- De-registers all listeners for eventName. Returns the contract, so calls may be chained.
- prototype . removeListener ( eventName , callback ) => Contract
- De-registers the specific callback for eventName. Returns the contract, so calls may be chained.
Types
There are many variable types available in Solidity, some which convert to and from JavaScript gracefully, and others that do not. Here are some note regarding passing and returning values in Contracts.
有许多可靠的变量类型可用,有些可以优雅地转换为JavaScript,有些则不能。下面有一些关于在合约中传递和返回值的注意事项。
Bytes
Bytes are available in fixed-length or dynamic-length variants. In both cases, the values are returned as a hex string and may be passed in as either a hex string or as an arrayish.
To convert the string into an array, use the arrayify() utility function.
字节有固定长度或动态长度变体。在这两种情况下,值都以十六进制字符串的形式返回,并且可以以十六进制字符串或数组的形式传入。
要将字符串转换为数组,请使用arrayify()实用函数。(ethers.utils.arrayify(string))
Integers
Integers in solidity are a fixed number of bits (aligned to the nearest byte) and are available in signed and unsigned variants.
For example, a uint256 is 256 bits (32 bytes) and unsigned. An int8 is 8 bits (1 byte) and signed.
When the type is 48 bits (6 bytes) or less, values are returned as a JavaScript Number, since Javascript Numbers are safe to use up to 53 bits.
Any types with 56 bits (7 bytes) or more will be returned as a BigNumber, even if the value is within the 53 bit safe range.
When passing numeric values in, JavaScript Numbers, hex strings or any BigNumber is acceptable (however, take care when using JavaScript Numbers and performing mathematical operations on them).
The uint and int types are aliases for uint256 and int256, respectively.
整数是固定数量的位(与最近的字节对齐),有符号和无符号变体。
例如,uint256是256位(32字节)和无符号的。int8是8位(1字节)和带符号的。
当类型为48位(6字节)或更少时,值作为JavaScript数字返回,因为JavaScript数字可以安全地使用最多53位。
任何具有56位(7字节)或更多的类型都将作为一个大数字返回,即使该值在53位安全范围内。
传入数值时,JavaScript数字、十六进制字符串或任何BigNumber都是可以接受的(但是,在使用JavaScript数字并对其执行数学操作时要小心)。
uint和int类型分别是uint256和int256的别名。
Strings
For short strings, many Contracts use a bytes32 to encode a null-terminated string representation, rather than a length-prefixed representation, so the formatBytes32String and parseBytes32String utility functions can be used to handle this conversion.
To convert between the two dynamic types, strings and bytes, the toUtf8Bytes() and toUtf8String() utility functions can be used.
对于短字符串,许多合约使用bytes32来编码以null结尾的字符串表示,而不是长度为前缀的表示,因此可以使用formatBytes32String和parseBytes32String实用函数来处理这种转换。
要在两个动态类型(字符串和字节)之间进行转换,可以使用toUtf8Bytes()和toUtf8String()实用函数。
Structs
Structs can be specified as Objects with their named properties, or as an Array, the same length as the struct.
Constant methods which return a single item, return that item directly. If the method returns multiple values then an object is returned which can be accessed by either the named properties or by their indices, in which case both point to the same instance.
结构可以指定为具有命名属性的对象,也可以指定为与结构相同长度的数组。
常量方法返回单个项目,直接返回该项目。如果该方法返回多个值,那么将返回一个对象,该对象可以被命名属性或其索引访问,在这种情况下,它们都指向相同的实例。
/** * Contract Methods * * function oneItem() public view returns (uint256 param1); * function twoItems() public view returns (uint256 param1, uint256 param2); * */ let resultOne = await oneItem(); console.log(resultOne); // 1337 let resultTwo = await twoItems(); console.log(resultTwo); // { // "param1": 1337, // "param2": 42, // 0: 1337, // 1: 42, // length: 2 // } assert.ok(resultTwo[0] === resultTwo.param1); assert.ok(resultTwo[1] === resultTwo.param2);
Filtering Events
On every contract, there is a filters
property, which can be used to generate an event filter. And event filter can be passed into the on(eventName)
of a contract.
在每个合约上,都有一个filters属性,它可以用来生成一个事件过滤器。事件筛选器可以传递到合约的on(event name)中。
Find all ERC-20 transfers to myAddress
// The null field indicates any value matches, this specifies // "any Transfer from any to myAddress" let filter = contract.Transfer(null, myAddress); // Listen for our filtered results contract.on(filter, (from, to, value) => { console.log('I received ' + value.toString() + ' tokens to ' + to); });
Application Binary Interface (ABI)特别
Each Contract has a description of its interface, which describes each function and event.
The Solidity compiler generates the ABI in a JSON format, which can be used as a JSON string or parsed as a JavaScript Object. This is generated by the compiler and can be loaded as a file, or copied into the source code.
The ABI may also be specified using Human-Readable ABI, which is much easier to use when typing in an ABI by hand, for example, as well as easier to read. This is simply an array of strings, each of which is the Solidity function or event signature.
每个合约都有其接口的描述,接口描述了每个功能和事件。
Solidity编译器以JSON格式生成ABI,该格式可以作为JSON字符串使用,也可以作为JavaScript对象解析。这是由编译器生成的,可以作为文件加载,也可以复制到源代码中。
ABI也可以使用人类可读的ABI来指定,例如,手工输入ABI时使用ABI更容易,也更容易阅读。这只是一个字符串数组,每个字符串都是实体函数或事件签名。
Human-Readable ABI (好!!!!!)
let ABI = [ "event Transfer(address from, address to, uint amount)", "function transfer(address to, uint amount)", "function symbol() returns (string)" ]