.. _api-contract: Contracts ********* This API provides a graceful connection to a contract deployed on the blockchain, simplifying calling and querying its functions and handling all the binary protocol and conversion as necessarily. The Contract object is a meta-class, so many of the functions it will have are not defined until it is instantiated with an application binary interface (ABI) which is usually generated by a compiler, such as Solidity. To better see this demonstrated, see the `example`_ below. :: var Contract = ethers.Contract; ----- Connecting to a Contract ======================== new :sup:`ethers` . Contract ( addressOrName , interface , providerOrSigner ) Connects to the contract at *addressOrName* defined by *interface*, which may be a JSON string or the parsed object. The *providerOrSigner* may be any instance of the following: :ref:`Wallet ` The wallet will be used to sign and send transactions, and estimates and calls will use the wallet address. :ref:`Provider ` Gas estimates and constant functions can be called (but without an address) and event callbacks may be registered. `Custom Signer`_ For much more control over how and when signing and sending transaction occurs and to defer address behaviour. ----- Prototype ========= The prototype will contain all the methods and events defined in the **interface**. The result of all contant methods are a :ref:`Promise ` which resolve to the result as a tuple, optionally with the parameters accessible by name, if named in the ABI. The result of all non-constant methods are a :ref:`Promise ` which resolve to the :ref:`transaction ` that was sent to the network. Name collisions with the built-in properties (below) will not be overwritten. Instead, they must be accessed through the **functions** or **events** property. Due to signature overloading, multiple functions can have the same name. The first function specifed in the ABI will be bound to its name. To access overloaded functions, use the full typed signature of the functions (e.g. ``contract["foobar(address,uint256)"]``). :sup:`prototype` . address The address (or ENS name) of the contract. :sup:`prototype` . interface The :ref:`Interface ` meta-class of the parsed ABI. Generally, this should not need to be accessed directly. :sup:`prototype` . functions . *functionName* An object that maps each ABI function name to a function that will either call (for contant functions) or sign and send a transaction (for non-constant functions) :sup:`prototype` . estimate . *functionName* An object that maps each ABI function name to a function that will estimate the cost the provided parameters. :sup:`prototype` . events . on\ *eventname* An object that maps each ABI event name (lower case, with the "on" prefix) to a callback that is triggered when the event occurs. :sup:`prototype` . connect ( providerOrSigner ) Create a new instance of the Contract connected as the new *providerOrSigner*. .. _example: Examples -------- *Example Contract and Interface* :: /** * contract SimpleStore { * * event valueChanged(address author, string oldValue, string newValue); * * address _author; * string _value; * * function setValue(string value) public { * _author = msg.sender; * valueChanged(_author, _value, value); * _value = value; * } * * function getValue() constant public returns (string value) { * return _value; * } * * function getAuthorAndValue() constant public returns (address author, string value) { * return (_author, _value); * } * } */ // The interface from the Solidity compiler var ethers = require('ethers'); var abi = [ { "constant":true, "inputs":[], "name":"getValue", "outputs":[{"name":"value","type":"string"}], "payable":false, "type":"function" }, { "constant":true, "inputs":[], "name":"getAuthorAndValue", "outputs":[ {"name":"author","type":"address"}, {"name":"value","type":"string"} ], "payable":false, "type":"function" }, { "constant":false, "inputs":[{"name":"value","type":"string"}], "name":"setValue", "outputs":[], "payable":false, "type":"function" }, { "anonymous":false, "inputs":[ {"indexed":false,"name":"oldValue","type":"string"}, {"indexed":false,"name":"newValue","type":"string"} ], "name":"valueChanged", "type":"event" } ]; var address = '0x2BA27534A8814765795f1Db8AEa01d5dbe4112d9'; var provider = ethers.providers.getDefaultProvider('ropsten'); var contract = new ethers.Contract(address, abi, provider); *Example Constant Function* -- **getAuthorAndValue ( ) returns ( address author , string value )** :: var callPromise = contract.getValue(); callPromise.then(function(result) { console.log('Positional argument [0]; author: ' + result[0]); console.log('Positional argument [1]; value: ' + result[1]); console.log('Keyword argument [author]; author: ' + result.author); console.log('Keyword argument [value]; value: ' + result.value); }); // These are identical to the above call // var callPromise = contract.functions.getValue(); // var callPromise = contract['getValue()'](); // var callPromise = contract.functions['getValue()'](); // var callPromise = contract['getValue()'](); *Example Constant Function with Single Return Value* -- **getValue ( ) returns ( string value )** :: var callPromise = contract.getValue(); callPromise.then(function(value) { console.log('Single Return Value:' + value); }); *Example Non-Constant Function* -- **setValue ( string value )** :: // to call a non-constant function, the contract needs to be // initialized with a wallet or a customSigner var provider = ethers.providers.getDefaultProvider('ropsten'); var address = '0x2BA27534A8814765795f1Db8AEa01d5dbe4112d9'; var privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123'; var wallet = new ethers.Wallet(privateKey, provider); var contract = new ethers.Contract(address, abi, wallet); var sendPromise = contract.setValue("Hello World"); sendPromise.then(function(transaction) { console.log(transaction); }); // This is identical to the above send // var sendPromise = contract.functions.setValue("Hello World"); // Overriding parameters; any of these are optional and get passed // as an additional parameter after all function parameters. var overrideOptions = { gasLimit: 250000, gasPrice: 9000000000, nonce: 0, value: ethers.utils.parseEther('1.0') }; var sendPromise = contract.setValue("Hello World", overrideOptions); *Example Event Registration* -- **valueChanged ( author , value )** :: // Register for events contract.onvaluechanged = function(oldValue, newValue) { console.log('oldValue: ' + oldValue); console.log('newValue: ' + newValue); }; // This is identical to the above event registry // contract.events.onvaluechanged = function(author, value) { ... *Example Non-Constant Gas Estimate* :: // to get the gas estimate, the contract needs to be // initialized with a wallet or a customSigner var provider = ethers.providers.getDefaultProvider('ropsten'); var privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123'; var wallet = new ethers.Wallet(privateKey, provider); var contract = new ethers.Contract(address, abi, wallet); var estimatePromise = contract.estimate.setValue("Hello World"); estimatePromise.then(function(gasCost) { // gasCost is returned as BigNumber console.log('Estimated Gas Cost: ' + gasCost.toString()); }); ----- Result Types ============ There are many variable types avaiable in Solidity, some which work well in JavaScript and others that do not. Here are some note regarding passing and returning values in Contracts. 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 amd performing mathematic operations on them). The **uint** and **int** types are aliases for **uint256** and **int256**, respectively. Strings ------- Strings work fine and require no special care. To convert between strings and bytes, which may occasionally come up, use the :ref:`utils.toUtf8Bytes() ` and :ref:`utils.toUtf8String() ` utility functions. 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 :ref:`arrayish `. To convert the string into an array, use the :ref:`utils.arrayify() ` utility function. Arrays ------ Arrays work fine and require no special care. ----- Deploying a Contract ==================== To deploy a contract to the Ethereum network, you must have its bytecode and its application binary interface (ABI), usually generated from the Solidity compiler. :sup:`Contract` . getDeployTransaction ( bytecode , interface , ... ) Generate the transaction needed to deploy the contract specified by *bytecode* and *interface*. Any additional parameters the constructor take should also be passed in. *Examples* ---------- :: /** * contract Example { * * string _value; * * // Constructor * function Example(string value) { * _value = value; * } * } */ var ethers = require('ethers'); // The interface from Solidity var abi = '[{"inputs":[{"name":"value","type":"string"}],"type":"constructor"}]'; // The bytecode from Solidity var bytecode = "0x6060604052341561000c57fe5b60405161012d38038061012d83398101604052" + "8080518201919050505b806000908051906020019061003f929190610047565b" + "505b506100ec565b828054600181600116156101000203166002900490600052" + "602060002090601f016020900481019282601f1061008857805160ff19168380" + "011785556100b6565b828001600101855582156100b6579182015b8281111561" + "00b557825182559160200191906001019061009a565b5b5090506100c3919061" + "00c7565b5090565b6100e991905b808211156100e55760008160009055506001" + "016100cd565b5090565b90565b6033806100fa6000396000f30060606040525b" + "fe00a165627a7a72305820041f440021b887310055b6f4e647c2844f4e1c8cf1" + "d8e037c72cd7d0aa671e2f0029"; // Notice we pass in "Hello World" as the parameter to the constructor var deployTransaction = ethers.Contract.getDeployTransaction(bytecode, abi, "Hello World"); console.log(deployTransaction); // { // data: "0x6060604052341561000c57fe5b60405161012d38038061012d83398101604052" + // "8080518201919050505b806000908051906020019061003f929190610047565b" + // "505b506100ec565b828054600181600116156101000203166002900490600052" + // "602060002090601f016020900481019282601f1061008857805160ff19168380" + // "011785556100b6565b828001600101855582156100b6579182015b8281111561" + // "00b557825182559160200191906001019061009a565b5b5090506100c3919061" + // "00c7565b5090565b6100e991905b808211156100e55760008160009055506001" + // "016100cd565b5090565b90565b6033806100fa6000396000f30060606040525b" + // "fe00a165627a7a72305820041f440021b887310055b6f4e647c2844f4e1c8cf1" + // "d8e037c72cd7d0aa671e2f002900000000000000000000000000000000000000" + // "0000000000000000000000002000000000000000000000000000000000000000" + // "0000000000000000000000000b48656c6c6f20576f726c640000000000000000" + // "00000000000000000000000000" // } // Connect to the network var provider = ethers.providers.getDefaultProvider(); // Create a wallet to deploy the contract with var privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123'; var wallet = new ethers.Wallet(privateKey, provider); // Send the transaction var sendPromise = wallet.sendTransaction(deployTransaction); // Get the transaction sendPromise.then(function(transaction) { console.log(transaction); }); ----- .. _custom-signer: Custom Signer ============= The simplest way to specify a signer is to simply use an instance of a wallet. However, if more fine-grained control is required, a custom signer allow deferring the address, signing and sending transactions. A signer can be any object with: :sup:`object` . getAddress() *Required.* Which must return a valid address or a :ref:`Promise ` which will resolve to a valid address or reject an error. :sup:`object` . provider *Required.* A provider that will be used to connect to the Ethereum blockchain to issue calls, listen for events and possibly send transaction. :sup:`object` . estimateGas ( transaction ) *Optional.* If this is not defined, the provider is queries directly, after populating the address using *getAddress()*. The result must be a :ref:`Promise ` which resolves to the :ref:`BigNumber ` estimated gas cost. :sup:`object` . sendTransaction ( transaction ) *Optional.* If this is defined, it is called instead of sign and is expected to populate *nonce*, *gasLimit* and *gasPrice*. The result must be a :ref:`Promise ` which resolves to the sent transaction, or rejects on failure. :sup:`object` . sign ( transaction ) *Optional.* If this is defined, it is called to sign a transaction before using the provider to send it to the network. The result may be a valid :ref:`hex string ` or a promise which will resolve to a valid :ref:`hex string ` signed transaction or reject on failure. *Examples* ---------- :: var privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123'; var wallet = new ethers.Wallet(privateKey); function getAddress() { return new Promise(function(resolve, reject) { // Some asynchronous method; some examples // - request which account from the user // - query a database // - wait for another contract to be mined var address = wallet.address; resolve(address); }); } function sign(transaction) { return new Promise(function(resolve, reject) { // Some asynchronous method; some examples // - prompt the user to confirm or decline // - check available funds and credits // - request 2FA over SMS var signedTransaction = wallet.sign(transaction); resolve(signedTransaction); }); } var customSigner = { getAddress: getAddress, provider: ethers.providers.getDefaultProvider(), sign: sign } ----- .. EOF