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 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:
- Wallet
- The wallet will be used to sign and send transactions, and estimates and calls will use the wallet address.
- 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 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 Promise which resolve to the 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)"]
).
- prototype . address
- The address (or ENS name) of the contract.
- prototype . interface
- The Interface meta-class of the parsed ABI. Generally, this should not need to be accessed directly.
- 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)
- prototype . estimate . functionName
- An object that maps each ABI function name to a function that will estimate the cost the provided parameters.
- prototype . events . oneventname
- An object that maps each ABI event name (lower case, with the “on” prefix) to a callback that is triggered when the event occurs.
- prototype . connect ( providerOrSigner )
- Create a new instance of the Contract connected as the new providerOrSigner.
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 utils.toUtf8Bytes() and 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 arrayish.
To convert the string into an array, use the 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.
- 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¶
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:
- object . getAddress()
Required.
Which must return a valid address or a Promise which will resolve to a valid address or reject an error.
- 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.
- 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 Promise which resolves to the BigNumber estimated gas cost.
- 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 Promise which resolves to the sent transaction, or rejects on failure.
- 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 hex string or a promise which will resolve to a valid 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
}