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)" ]