Removing legacy docs hosted source.
This commit is contained in:
parent
750ba7ff8c
commit
4c4830670a
@ -1,624 +0,0 @@
|
||||
Low-Level API
|
||||
**************
|
||||
|
||||
These are advanced, low-level API features that should, for most people not be
|
||||
necessary to worry about.
|
||||
|
||||
They are lightly documented here, and in the future will have more documentation,
|
||||
but the emphasis at this point is documenting the more :ref:`common methods <api>`.
|
||||
|
||||
-----
|
||||
|
||||
.. _api-hdnode:
|
||||
|
||||
HDNode
|
||||
======
|
||||
|
||||
A *Hierarchical Deterministic Wallet* represents a large tree of private keys
|
||||
which can reliably be reproduced from an initial seed. Each node in the tree
|
||||
is represended by an HDNode which can be descended into.
|
||||
|
||||
A *mnemonic phrase* represents a simple way to generate the initial seed.
|
||||
|
||||
See the `BIP 32 Specification`_ to learn more about HD Wallets and hardened vs
|
||||
non-hardened nodes.
|
||||
|
||||
See the `BIP 39 Specification`_ to learn more about Mnemonic Phrases.
|
||||
|
||||
Creating Instances
|
||||
------------------
|
||||
|
||||
:sup:`ethers . HDNode` **. fromMnemonic** ( mnemonic )
|
||||
Create an HDNode from a *mnemonic* phrase.
|
||||
|
||||
:sup:`ethers . HDNode` **. fromSeed** ( seed )
|
||||
Create an HDNode from a seed.
|
||||
|
||||
|
||||
Prototype
|
||||
---------
|
||||
|
||||
:sup:`prototype` **. privateKey**
|
||||
The :ref:`hex string <hexstring>` private key for this node.
|
||||
|
||||
:sup:`prototype` **. publicKey**
|
||||
The (compressed) public key for this node.
|
||||
|
||||
:sup:`prototype` **. chainCode**
|
||||
The chain code for this node.
|
||||
|
||||
:sup:`prototype` **. index**
|
||||
The index (from the parent) of this node (0 for the master node).
|
||||
|
||||
:sup:`prototype` **. depth**
|
||||
The depth within th hierarchy of this node.
|
||||
|
||||
:sup:`prototype` **. derivePath** ( path )
|
||||
Derive the path from this node. Path is slash (**/**) delimited path components.
|
||||
The first component may be "m" for master (which enforces the starting node is
|
||||
infact a master node) and each subsequent path component should be a positive
|
||||
integer (up to 31 bits), which can optionally include an apostrophe (**'**) to
|
||||
indicate hardened derivation for that path components. See below for some examples.
|
||||
|
||||
|
||||
Static Methods
|
||||
--------------
|
||||
|
||||
:sup:`ethers . HDNode` **. mnemonicToEntropy** ( mnemonic )
|
||||
Convert a *mnemonic* to its binary entropy. (throws an error if the checksum
|
||||
is invalid)
|
||||
|
||||
:sup:`ethers . HDNode` **. entropyToMnemonic** ( entropy )
|
||||
Convert the binary *entropy* to the mnemonic phrase.
|
||||
|
||||
:sup:`ethers . HDNode` **. mnemonicToSeed** ( mnemonic )
|
||||
Compute the BIP39 seed from *mnemonic*.
|
||||
|
||||
:sup:`ethers . HDNode` **. isValidMnemonic** ( string )
|
||||
Returns true if and only if the string is a valid mnemonic (including
|
||||
the checksum)
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
var HDNode = require('ethers').HDNode;
|
||||
|
||||
var mnemonic = "radar blur cabbage chef fix engine embark joy scheme fiction master release";
|
||||
|
||||
var masterNode = HDNode.fromMnemonic(mnemonic);
|
||||
|
||||
var standardEthereum = masterNode.derivePath("m/44'/60'/0'/0/0");
|
||||
|
||||
-----
|
||||
|
||||
.. _api-interface:
|
||||
|
||||
Interface
|
||||
=========
|
||||
|
||||
The Interface Object is a meta-class that accepts a Solidity (or compatible)
|
||||
Application Binary Interface (ABI) and populates functions to deal with encoding
|
||||
and decoding the parameters to pass in and results returned.
|
||||
|
||||
Creating an Instance
|
||||
--------------------
|
||||
|
||||
new :sup:`ethers` . Interface ( abi )
|
||||
Returns a new instance and populates the properties with the ABI constructor,
|
||||
methods and events. The *abi* may be either a JSON string or the parsed JSON
|
||||
Object.
|
||||
|
||||
|
||||
Prototype
|
||||
---------
|
||||
|
||||
:sup:`prototype` . abi
|
||||
A **copy** of the ABI is returned, modifying this object will not alter the ABI.
|
||||
|
||||
:sup:`prototype` . functions
|
||||
An object of the functions available in the ABI, by name. (collissions are dropped)
|
||||
|
||||
:sup:`prototype` . events
|
||||
An object of the events available in the ABI, by name. (collisions are dropped)
|
||||
|
||||
:sup:`prototype` . deployFunction ( bytecode [ , params... ])
|
||||
The function to deploy the contract (compiled to *bytecode*) to the network, passing
|
||||
*params* into the ABI constructor. If the ABI does not have a constructor, a default
|
||||
one is generated.
|
||||
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
**Creating an Interface Instance** ::
|
||||
|
||||
var Interface = require('ethers').Interface;
|
||||
|
||||
var abi = [
|
||||
{
|
||||
constant: true,
|
||||
inputs:[],
|
||||
name: "getValue",
|
||||
outputs:[ { name: "value", type: "string"} ],
|
||||
type: "function"
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [ { name: "value", type: "string" } ],
|
||||
name: "setValue",
|
||||
outputs: [],
|
||||
type: "function"
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs:[
|
||||
{ indexed:false, name: "oldValue", type: "string" },
|
||||
{ indexed:false, name: "newValue", type: "string" }
|
||||
],
|
||||
name: "valueChanged",
|
||||
type: "event"
|
||||
}
|
||||
];
|
||||
|
||||
// NOTE: "interface" is a reserved keyword in JavaScript
|
||||
|
||||
var iface = new Interface(abi)
|
||||
|
||||
**Call (Constant) Functions** ::
|
||||
|
||||
var getValueInfo = iface.functions.getValue();
|
||||
|
||||
console.log(getValueInfo);
|
||||
// {
|
||||
// name: "getValue",
|
||||
// signature: "getValue()",
|
||||
// data: "0x20965255",
|
||||
// parse: function(result),
|
||||
// type: "call"
|
||||
// }
|
||||
|
||||
// Here is the result of:
|
||||
// provider.call({
|
||||
// to: "0x954De93D9f1Cd1e2e3AE5964F614CDcc821Fac64",
|
||||
// data: getValue.data,
|
||||
// }).then(function(result) {
|
||||
// console.log(result);
|
||||
// });
|
||||
var getDataResult = "0x0000000000000000000000000000000000000000000000000000000000000020" +
|
||||
"000000000000000000000000000000000000000000000000000000000000000b" +
|
||||
"48656c6c6f20576f726c64000000000000000000000000000000000000000000"
|
||||
|
||||
console.log(getValueInfo.parse(getDataResult));
|
||||
// {
|
||||
// 0: "Hello World",
|
||||
// value: "Hello World",
|
||||
// length: 1
|
||||
// }
|
||||
|
||||
|
||||
**Transaction (Non-Constant) Functions** ::
|
||||
|
||||
var setValueInfo = iface.functions.setValue("Foobar!");
|
||||
|
||||
console.log(setValueInfo);
|
||||
// {
|
||||
// name: "setValue",
|
||||
// signature: "setValue(string)",
|
||||
// data: "0x93a09352" +
|
||||
// "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||
// "0000000000000000000000000000000000000000000000000000000000000007" +
|
||||
// "466f6f6261722100000000000000000000000000000000000000000000000000"
|
||||
// type: "transaction"
|
||||
// }
|
||||
|
||||
// To send this to the network, you would sign and send the transaction:
|
||||
// {
|
||||
// to: "0x954De93D9f1Cd1e2e3AE5964F614CDcc821Fac64",
|
||||
// data: setValueInfo.data,
|
||||
// gasLimit: someGasLimit,
|
||||
// gasPrice: someGasPrice,
|
||||
// nonce: yourTransactionCountForYourAddress
|
||||
// }
|
||||
|
||||
**Events** ::
|
||||
|
||||
var ethers = require('ethers');
|
||||
var Interface = ethers.Interface;
|
||||
var abi = [
|
||||
{
|
||||
anonymous: false,
|
||||
inputs:[
|
||||
{ indexed:true, name: "from", type: "address" },
|
||||
{ indexed:true, name: "to", type: "address" },
|
||||
{ indexed:false, name: "value", type: "uint256" }
|
||||
],
|
||||
name: "Transfer",
|
||||
type: "event"
|
||||
}
|
||||
];
|
||||
// NOTE: "interface" is a reserved keyword in JavaScript
|
||||
var iface = new Interface(abi)
|
||||
var transferInfo = iface.events.Transfer();
|
||||
console.log(transferInfo);
|
||||
// EventDescription {
|
||||
// inputs:
|
||||
// [ { indexed: true, name: 'from', type: 'address' },
|
||||
// { indexed: true, name: 'to', type: 'address' },
|
||||
// { indexed: false, name: 'value', type: 'uint256' } ],
|
||||
// name: 'Transfer',
|
||||
// signature: 'Transfer(address,address,uint256)',
|
||||
// topics: [ '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' ],
|
||||
// parse: [Function] }
|
||||
|
||||
|
||||
// To listen for this event:
|
||||
var provider = ethers.providers.getDefaultProvider();
|
||||
provider.on(transferInfo.topics, function(log) {
|
||||
// Parse event data (only returns the non-indexed entries)
|
||||
var result = transferInfo.parse(log.data);
|
||||
console.log('non-indexed entries: ', result);
|
||||
|
||||
// non-indexed entries: Result {
|
||||
// '0': Indexed { indexed: true, hash: null },
|
||||
// '1': Indexed { indexed: true, hash: null },
|
||||
// '2': BigNumber { _bn: <BN: 1db3c5934d11e3c0000> },
|
||||
// from: Indexed { indexed: true, hash: null },
|
||||
// to: Indexed { indexed: true, hash: null },
|
||||
// value: BigNumber { _bn: <BN: 1db3c5934d11e3c0000> },
|
||||
// length: 3 }
|
||||
|
||||
|
||||
// Parse event topics and data (returns all entries)
|
||||
// Note: Any indexed entry which is not a 32 byte value is hashed.
|
||||
// Dynamic arrays are hashed as a static sized array.
|
||||
result = transferInfo.parse(log.topics, log.data);
|
||||
console.log('all entries: ', result);
|
||||
|
||||
// all entries: Result {
|
||||
// '0': '0x0000000000000000000000000000000000000000',
|
||||
// '1': '0x92239D0512c313E1b001b3996707F822a76C0901',
|
||||
// '2': BigNumber { _bn: <BN: 1db3c5934d11e3c0000> },
|
||||
// from: '0x0000000000000000000000000000000000000000',
|
||||
// to: '0x92239D0512c313E1b001b3996707F822a76C0901',
|
||||
// value: BigNumber { _bn: <BN: 1db3c5934d11e3c0000> },
|
||||
// length: 3 }
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
ABI Coder
|
||||
=========
|
||||
|
||||
Creating Instances
|
||||
------------------
|
||||
|
||||
new :sup:`ethers . utils` **. AbiCoder** ( [ coerceFunc ] )
|
||||
Create a new ABI Coder object, which calls *coerceFunc* for each parsed value
|
||||
during decoding. The *coerceFunc* should have the signature: ``function(type, value)``.
|
||||
|
||||
Static Properties
|
||||
-----------------
|
||||
|
||||
:sup:`ethers . utils` **. defaultAbiCoder**
|
||||
A default instance of the coder which can be used, which has a *coerceFunc*
|
||||
which will call ``toNumber()`` on BigNumbers whose **type** is less than
|
||||
53 bits and is safe for JavaScript Number instances.
|
||||
|
||||
Prototype
|
||||
---------
|
||||
|
||||
:sup:`prototype` . encode ( [ names , ] types , values )
|
||||
Returns a :ref:`hex string <hexstring>` of the *values* encoded as the *types*.
|
||||
If names is provided, *values* may contain named keys for tuples, otherwise
|
||||
each tuple expects an Array. Throws if a value is invalid for the type.
|
||||
|
||||
:sup:`prototype` . decode ( [ names , ] types , data )
|
||||
Returns an Object by parsing *data* assuming *types*, with each parameter
|
||||
accessible as apositional parameters. If *names* is provided, each
|
||||
parameter is also accessible by its name. Throws if *data* is invalid
|
||||
for the *types*.
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Provider (Sub-Classing)
|
||||
=======================
|
||||
|
||||
See the :ref:`Provider API <api-provider>` for more common usage. This documentation
|
||||
is designed for developers that are sub-classing Provider.
|
||||
|
||||
Static Methods
|
||||
--------------
|
||||
|
||||
:sup:`Provider` . inherits ( childProvider )
|
||||
Set up *childProvider* as an provider, inheriting the parent prototype and
|
||||
set up a prototype.inherits on the *childProvider*.
|
||||
|
||||
:sup:`Provider` . fetchJSON ( url , body , processFunc )
|
||||
Convenience method for returning a :ref:`Promise <promise>` with the result of fetching JSON
|
||||
from a *url* with an optional *body*. The optional *processFunc* is called on
|
||||
the parsed JSON before being passed to the Promise's resolve. (throwing an error
|
||||
in the *processFunc* will cause the Promise to reject)
|
||||
|
||||
Prototype
|
||||
---------
|
||||
|
||||
:sup:`prototype` . perform ( method , params )
|
||||
The only method needed to override in a subclass. All values are sanitized
|
||||
and defaults populated in params and the result is sanitized before returning.
|
||||
Returns a :ref:`Promise <promise>`, see the example below for overview of
|
||||
*method* and *params*.
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
var ethers = require('ethers');
|
||||
var utils = ethers.utils;
|
||||
var Provider = ethers.providers.Provider;
|
||||
|
||||
// The new provider Object
|
||||
function DemoProvider(testnet, somethingElse) {
|
||||
Provide.call(this, testnet);
|
||||
|
||||
utils.defineProperty(this, 'somethingElse', somethingElse);
|
||||
}
|
||||
|
||||
// Inherit the Provider
|
||||
Provider.inherits(DemoProvider);
|
||||
|
||||
// Override perform
|
||||
utils.defineProperty(DemoProvider.prototype, 'perform', function(method, params) {
|
||||
switch (method) {
|
||||
case 'getBlockNumber':
|
||||
// Params:
|
||||
// { }
|
||||
|
||||
case 'getGasPrice':
|
||||
// Params:
|
||||
// { }
|
||||
|
||||
case 'getBalance':
|
||||
// Params:
|
||||
// {
|
||||
// address: address,
|
||||
// blockTag: blockTag
|
||||
// }
|
||||
|
||||
case 'getTransactionCount':
|
||||
// Params:
|
||||
// {
|
||||
// address: address,
|
||||
// blockTag: blockTag
|
||||
// }
|
||||
|
||||
case 'getCode':
|
||||
// Params:
|
||||
// {
|
||||
// address: address,
|
||||
// blockTag: blockTag
|
||||
// }
|
||||
|
||||
case 'getStorageAt':
|
||||
// Params:
|
||||
// {
|
||||
// address: address,
|
||||
// position: hexString,
|
||||
// blockTag: blockTag
|
||||
// }
|
||||
|
||||
case 'sendTransaction':
|
||||
// Params:
|
||||
// {
|
||||
// signedTransaction: hexString
|
||||
// }
|
||||
|
||||
case 'getBlock':
|
||||
// Params:
|
||||
// Exactly one of the following will be specified, the other will be absent
|
||||
// {
|
||||
// blockHash: blockHash,
|
||||
// blockTag: blockTag
|
||||
// }
|
||||
|
||||
case 'getTransaction':
|
||||
// Params:
|
||||
// {
|
||||
// transactionHash: hexString
|
||||
// }
|
||||
|
||||
case 'getTransactionReceipt':
|
||||
// Params:
|
||||
// {
|
||||
// transactionHash: hexString
|
||||
// }
|
||||
|
||||
case 'call':
|
||||
// Params:
|
||||
// {
|
||||
// transaction: See Transaction Requests (on Providers API)
|
||||
// }
|
||||
|
||||
case 'estimateGas':
|
||||
// Params:
|
||||
// {
|
||||
// transaction: See Transaction Requests (on Providers API)
|
||||
// }
|
||||
|
||||
case 'getLogs':
|
||||
// Params:
|
||||
// {
|
||||
// address: address,
|
||||
// fromBlock: blockTag,
|
||||
// toBlock: blockTag,
|
||||
// topics: array (possibly nested) of topics
|
||||
// }
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Promise.reject(new Error('not implemented - ' + method));
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
Signing Key
|
||||
===========
|
||||
|
||||
The SigningKey interface provides an abstraction around the
|
||||
*secp256k1 elliptic curve cryptography* library, which signs digests,
|
||||
computes public keys from private keys and performs *ecrecover* which
|
||||
computes a public key from a digest and a signature.
|
||||
|
||||
|
||||
Creating Instances
|
||||
------------------
|
||||
|
||||
new :sup:`ethers` . SigningKey ( privateKey )
|
||||
Create a new SigningKey and compute the corresponding public key and address.
|
||||
A private key may be a any :ref:`hex string <hexstring>` or an
|
||||
:ref:`Arrayish <api-arrayish>` representing 32 bytes.
|
||||
|
||||
|
||||
Static Methods
|
||||
--------------
|
||||
|
||||
:sup:`SigningKey` . recover ( digest, r, s, recoveryParam )
|
||||
Given a message *digest* and the signature parameters *r*, *s*
|
||||
and *recoveryParam* compute the the address that signed the
|
||||
message.
|
||||
|
||||
:sup:`SigningKey` . getPublicKey ( publicOrPrivateKey [, compressed] )
|
||||
Given a *publicOrPrivateKey*, return the public key, optionally *compressed*.
|
||||
|
||||
**default:** *compressed*\ =false
|
||||
|
||||
:sup:`SigningKey` . publicKeyToAddress ( publicOrPrivateKey )
|
||||
Convert a *publicOrPrivateKey* to an Ethereum address.
|
||||
|
||||
Prototype
|
||||
---------
|
||||
|
||||
:sup:`prototype` . privateKey
|
||||
The private key.
|
||||
|
||||
:sup:`prototype` . publicKey
|
||||
The compressed public key.
|
||||
|
||||
:sup:`prototype` . address
|
||||
The Ethereum address for this key pair.
|
||||
|
||||
:sup:`prototype` . signDigest ( messageDigest )
|
||||
The compressed public key
|
||||
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
var ethers = require('ethers');
|
||||
var SigningKey = ethers._SigningKey;
|
||||
|
||||
var privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
|
||||
var signingKey = new SigningKey(privateKey);
|
||||
|
||||
console.log('Address: ' + signingKey.address);
|
||||
// "Address: 0x14791697260E4c9A71f18484C9f997B308e59325"
|
||||
|
||||
var message = "Hello World";
|
||||
var messageBytes = ethers.utils.toUtf8Bytes(message);
|
||||
var messageDigest = ethers.utils.keccak256(messageBytes);
|
||||
|
||||
console.log("Digest: " + messageDigest);
|
||||
// "Digest: 0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba"
|
||||
|
||||
var signature = signingKey.signDigest(messageDigest);
|
||||
|
||||
console.log(signature);
|
||||
// {
|
||||
// recoveryParam: 0,
|
||||
// r: "0x79f56f3422dc67f57b2aeeb0b20295a99ec90420b203177f83d419c98beda7fe",
|
||||
// s: "0x1a9d05433883bdc7e6d882740f4ea7921ef458a61b2cfe6197c2bb1bc47236fd"
|
||||
// }
|
||||
|
||||
var recovered = SigningKey.recover(messageDigest, signature.r,
|
||||
signature.s, signature.recoveryParam);
|
||||
|
||||
console.log("Recovered: " + recovered);
|
||||
// "Recovered: 0x14791697260E4c9A71f18484C9f997B308e59325"
|
||||
|
||||
var publicKey = signingKey.publicKey;
|
||||
|
||||
console.log('Public Key: ' + publicKey);
|
||||
// "Public Key: 0x026655feed4d214c261e0a6b554395596f1f1476a77d999560e5a8df9b8a1a3515"
|
||||
|
||||
var compressedPublicKey = SigningKey.getPublicKey(publicKey, true);
|
||||
var uncompressedPublicKey = SigningKey.getPublicKey(publicKey, false);
|
||||
|
||||
console.log('Compressed: ' + compressedPublicKey);
|
||||
// "Compressed: 0x026655feed4d214c261e0a6b554395596f1f1476a77d999560e5a8df9b8a1a3515"
|
||||
|
||||
console.log('Uncompressed: ' + uncompressedPublicKey);
|
||||
// "Uncompressed: 0x046655feed4d214c261e0a6b554395596f1f1476a77d999560e5a8df9b8a1a35" +
|
||||
// "15217e88dd05e938efdd71b2cce322bf01da96cd42087b236e8f5043157a9c068e"
|
||||
|
||||
var address = SigningKey.publicKeyToAddress(publicKey);
|
||||
|
||||
console.log('Address: ' + address);
|
||||
// "Address: 0x14791697260E4c9A71f18484C9f997B308e59325"
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Recursive-Length Prefixed Encoding (RLP)
|
||||
========================================
|
||||
|
||||
This encoding method is used internally for several aspects of Ethereum, such as
|
||||
encoding transactions and determining contract addresses. For most developers this
|
||||
should not be necessary to use.
|
||||
|
||||
RLP can encode nested arrays, with data as :ref:`hex strings <hexstring>` and Uint8Array (or other non-Array
|
||||
:ref:`arrayish <api-arrayish>` objects). A decoded object will always have data represented as :ref:`hex strings <hexstring>` and
|
||||
Arrays.
|
||||
|
||||
See: https://github.com/ethereum/wiki/wiki/RLP
|
||||
|
||||
Static Methods
|
||||
--------------
|
||||
|
||||
:sup:`ethers . utils . RLP` . encode( object )
|
||||
Encodes an object as an RLP :ref:`hex string <hexstring>`. (throws an Error if the object contains
|
||||
invalid items)
|
||||
|
||||
:sup:`ethers . utils . RLP` . decode( hexStringOrArrayish )
|
||||
Decode *hexStringOrArrayish* into the encoded object. (throws an Error if
|
||||
invalid RLP-coded data)
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
var rlp = require('ethers').utils.RLP;
|
||||
|
||||
var object = [ ["0x42"], "0x1234", [ [], [] ] ];
|
||||
|
||||
var encoded = rlp.encode(object);
|
||||
console.log(encoded);
|
||||
// 0xc8c142821234c2c0c0
|
||||
|
||||
var decoded = rlp.decode(encoded);
|
||||
console.log(decoded);
|
||||
// [ [ '0x42' ], '0x1234', [ [], [] ] ]
|
||||
|
||||
-----
|
||||
|
||||
.. _BIP 32 Specification: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||
.. _BIP 39 Specification: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
|
||||
|
||||
.. EOF
|
@ -1,485 +0,0 @@
|
||||
.. _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 <api-wallet>`
|
||||
The wallet will be used to sign and send transactions, and
|
||||
estimates and calls will use the wallet address.
|
||||
|
||||
:ref:`Provider <api-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 <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 <promise>`
|
||||
which resolve to the :ref:`transaction <transactionrequest>` 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 <api-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() <api-utf8-strings>` and :ref:`utils.toUtf8String() <api-utf8-strings>`
|
||||
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 <api-arrayish>`.
|
||||
|
||||
To convert the string into an array, use the :ref:`utils.arrayify() <api-arrayish>` 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 <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 <promise>` which resolves to the
|
||||
:ref:`BigNumber <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 <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 <hexstring>` or a promise which will resolve to a valid
|
||||
:ref:`hex string <hexstring>` 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
|
@ -1,845 +0,0 @@
|
||||
.. _api-provider:
|
||||
|
||||
Providers API
|
||||
*************
|
||||
|
||||
A Provider abstracts a connection to the Ethereum blockchain, for issuing queries
|
||||
and sending state changing transactions.
|
||||
|
||||
Unlike the Web3 provider, there is no concept of accounts or signing entities in
|
||||
an Ethers Provider. It is simply a connection to the network, and cannot unlock
|
||||
accounts or sign and is unaware of your Ethereum addresses.
|
||||
|
||||
To manage state changing operations, you must use a :ref:`Wallet <api-wallet>`
|
||||
to sign transactions. If you pass a wallet in as a signer to
|
||||
a :ref:`Contract <api-contract>`, this is managed for you by the contract.
|
||||
|
||||
|
||||
-----
|
||||
|
||||
|
||||
Connecting to Ethereum
|
||||
======================
|
||||
|
||||
There are several ways to connect to the Ethereum blockchain:
|
||||
|
||||
new :sup:`ethers . providers` . EtherscanProvider( [ network ] [ , apiToken ] )
|
||||
Connect to the `Etherscan`_ blockchain `web service API`_.
|
||||
|
||||
**default:** *network*\ ='homestead', *apiToken*\ =null
|
||||
|
||||
new :sup:`ethers . providers` . JsonRpcProvider( [ url ] [ , network ] )
|
||||
Connect to the `JSON-RPC API`_ *url* of an Ethereum node, such as `Parity`_ or `Geth`_.
|
||||
|
||||
**default:** *url*\ ="http://localhost:8545/", *network*\ ='homestead'
|
||||
|
||||
new :sup:`ethers . providers` . InfuraProvider( [ network ] [ , apiAccessToken ] )
|
||||
Connect to the `INFURA`_ hosted network of Ethereum nodes.
|
||||
|
||||
**default:** *network*\ ='homestead', *apiAccessToken*\ =null
|
||||
|
||||
new :sup:`ethers . providers` . Web3Provider( web3Provider [ , network ] )
|
||||
Connect to an existing Web3 provider (e.g. `web3Instance.currentProvider`).
|
||||
|
||||
**default:** *network*\ ='homestead'
|
||||
|
||||
new :sup:`ethers . providers` . FallbackProvider( providers )
|
||||
Improves reliability by attempting each provider in turn, falling back to the
|
||||
next in the list if an error was encountered.
|
||||
|
||||
:sup:`ethers . providers` . getDefaultProvider( [ network ] )
|
||||
This automatically creates a FallbackProvider backed by INFURA and Etherscan; recommended
|
||||
|
||||
**default:** *network*\ ='homestead'
|
||||
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
var providers = require('ethers').providers;
|
||||
|
||||
// Connect to Ropsten (the test network)
|
||||
|
||||
// You may specify any of:
|
||||
// - boolean; true = ropsten, false = homestead
|
||||
// - object; { name: 'ropsten', chainId: 3 } (see ethers.networks);
|
||||
// - string; e.g. 'homestead', 'ropsten', 'rinkeby', 'kovan'
|
||||
var network = providers.networks.ropsten;
|
||||
|
||||
// Connect to INFUA
|
||||
var infuraProvider = new providers.InfuraProvider(network);
|
||||
|
||||
// Connect to Etherscan
|
||||
var etherscanProvider = new providers.EtherscanProvider(network);
|
||||
|
||||
// Creating a provider to automatically fallback onto Etherscan
|
||||
// if INFURA is down
|
||||
var fallbackProvider = new providers.FallbackProvider([
|
||||
infuraProvider,
|
||||
etherscanProvider
|
||||
]);
|
||||
|
||||
// This is equivalent to using the getDefaultProvider
|
||||
var provider = providers.getDefaultProvider(network)
|
||||
|
||||
// Connect to a local Parity instance
|
||||
var provider = new providers.JsonRpcProvider('http://localhost:8545', network);
|
||||
|
||||
// Connect to an injected Web3's provider (e.g. MetaMask)
|
||||
var web3Provider = new providers.Web3Provider(web3.currentProvider, network);
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Prototype
|
||||
=========
|
||||
|
||||
All properties are immutable, and reflect their default value if not specified, or if
|
||||
indirectly populated by child Objects.
|
||||
|
||||
.. _provider:
|
||||
|
||||
Provider
|
||||
--------
|
||||
|
||||
:sup:`prototype` . name
|
||||
The name of the network the provider is connected to (e.g. 'homestead', 'ropsten', 'rinkeby', 'kovan')
|
||||
|
||||
:sup:`prototype` . chainId
|
||||
The chain ID (or network ID) this provider is connected as; this is used by
|
||||
signers to prevent `replay attacks`_ across compatible networks
|
||||
|
||||
FallbackProvider :sup:`( inherits from Provider )`
|
||||
--------------------------------------------------
|
||||
|
||||
:sup:`prototype` . providers
|
||||
A **copy** of the array of providers (modifying this variable will not affect
|
||||
the providers attached)
|
||||
|
||||
JsonRpcProvider :sup:`( inherits from Provider )`
|
||||
-------------------------------------------------
|
||||
|
||||
:sup:`prototype` . url
|
||||
The JSON-RPC URL the provider is connected to
|
||||
|
||||
:sup:`prototype` . send ( method , params )
|
||||
Send the JSON-RPC *method* with *params*. This is useful for calling
|
||||
non-standard or less common JSON-RPC methods. A :ref:`Promise <promise>` is
|
||||
returned which will resolve to the parsed JSON result.
|
||||
|
||||
EtherscanProvider :sup:`( inherits from Provider )`
|
||||
---------------------------------------------------
|
||||
|
||||
:sup:`prototype` . apiToken
|
||||
The Etherscan API Token (or null if not specified)
|
||||
|
||||
InfuraProvider :sup:`( inherits from JsonRpcProvider )`
|
||||
-------------------------------------------------------
|
||||
|
||||
:sup:`prototype` . apiAccessToken
|
||||
The INFURA API Access Token (or null if not specified)
|
||||
|
||||
Web3Provider :sup:`( inherits from JsonRpcProvider )`
|
||||
-------------------------------------------------------
|
||||
|
||||
:sup:`prototype` . provider
|
||||
The underlying Web3-compatible provider from the Web3 library, for example
|
||||
an `HTTPProvider`_ or `IPCProvider`_. The only required method on a Web3 provider
|
||||
is:
|
||||
|
||||
*sendAsync ( method , params , callback )*
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Account Actions
|
||||
===============
|
||||
|
||||
:sup:`prototype` . getBalance ( addressOrName [ , blockTag ] )
|
||||
Returns a :ref:`Promise <promise>` with the balance (as a :ref:`BigNumber <bignumber>`) of
|
||||
*addressOrName* at *blockTag*. (See: :ref:`Block Tags <blocktag>`)
|
||||
|
||||
**default:** *blockTag*\ ="latest"
|
||||
|
||||
:sup:`prototype` . getTransactionCount ( addressOrName [ , blockTag ] )
|
||||
Returns a :ref:`Promise <promise>` with the number of sent transactions (as a Number) from
|
||||
*addressOrName* at *blockTag*. This is also the nonce required to send a new
|
||||
transaction. (See: :ref:`Block Tags <blocktag>`)
|
||||
|
||||
**default:** *blockTag*\ ="latest"
|
||||
|
||||
:sup:`prototype` . lookupAddress ( address )
|
||||
Returns a :ref:`Promise <promise>` which resolves to the ENS name (or null) that *address* resolves
|
||||
to.
|
||||
|
||||
:sup:`prototype` . resolveName ( ensName )
|
||||
Returns a :ref:`Promise <promise>` which resolves to the address (or null) of that the *ensName*
|
||||
resolves to.
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
var ethers = require('ethers');
|
||||
var providers = ethers.providers;
|
||||
|
||||
var provider = providers.getDefaultProvider('ropsten');
|
||||
|
||||
var address = "0x02F024e0882B310c6734703AB9066EdD3a10C6e0";
|
||||
|
||||
provider.getBalance(address).then(function(balance) {
|
||||
|
||||
// balance is a BigNumber (in wei); format is as a sting (in ether)
|
||||
var etherString = ethers.utils.formatEther(balance);
|
||||
|
||||
console.log("Balance: " + etherString);
|
||||
});
|
||||
|
||||
provider.getTransactionCount(address).then(function(transactionCount) {
|
||||
console.log("Total Transactions Ever Send: " + transactionCount);
|
||||
});
|
||||
|
||||
provider.resolveName("test.ricmoose.eth").then(function(address) {
|
||||
console.log("Address: " + address);
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
Blockchain Status
|
||||
=================
|
||||
|
||||
:sup:`prototype` . getBlockNumber ( )
|
||||
Returns a :ref:`Promise <promise>` with the latest block number (as a Number).
|
||||
|
||||
:sup:`prototype` . getGasPrice ( )
|
||||
Returns a :ref:`Promise <promise>` with the current gas price (as a :ref:`BigNumber <bignumber>`).
|
||||
|
||||
:sup:`prototype` . getBlock ( blockHashOrBlockNumber )
|
||||
Returns a :ref:`Promise <promise>` with the block at *blockHashorBlockNumber*. (See: :ref:`Block Responses <blockresponse>`)
|
||||
|
||||
:sup:`prototype` . getTransaction ( transactionHash )
|
||||
Returns a :ref:`Promise <promise>` with the transaction with *transactionHash*. (See: :ref:`Transaction Responses <transactionresponse>`)
|
||||
|
||||
:sup:`prototype` . getTransactionReceipt ( transactionHash )
|
||||
Returns a :ref:`Promise <promise>` with the transaction receipt with *transactionHash*.
|
||||
(See: :ref:`Transaction Receipts <transactionReceipt>`)
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
**Current State**\ ::
|
||||
|
||||
var provider = providers.getDefaultProvider();
|
||||
|
||||
provider.getBlockNumber().then(function(blockNumber) {
|
||||
console.log("Current block number: " + blockNumber);
|
||||
});
|
||||
|
||||
provider.getGasPrice().then(function(gasPrice) {
|
||||
// gasPrice is a BigNumber; convert it to a decimal string
|
||||
gasPriceString = gasPrice.toString();
|
||||
|
||||
console.log("Current gas price: " + gasPriceString);
|
||||
});
|
||||
|
||||
**Blocks**\ ::
|
||||
|
||||
var provider = providers.getDefaultProvider();
|
||||
|
||||
// Block Number
|
||||
provider.getBlock(3346773).then(function(block) {
|
||||
console.log(block);
|
||||
});
|
||||
|
||||
// Block Hash
|
||||
var blockHash = "0x7a1d0b010393c8d850200d0ec1e27c0c8a295366247b1bd6124d496cf59182ad";
|
||||
provider.getBlock(blockHash).then(function(block) {
|
||||
console.log(block);
|
||||
});
|
||||
|
||||
**Transactions**\ ::
|
||||
|
||||
var provider = providers.getDefaultProvider();
|
||||
|
||||
var transactionHash = "0x7baea23e7d77bff455d94f0c81916f938c398252fb62fce2cdb43643134ce4ed";
|
||||
|
||||
provider.getTransaction(transactionHash).then(function(transaction) {
|
||||
console.log(transaction);
|
||||
});
|
||||
|
||||
provider.getTransactionReceipt(transactionHash).then(function(transactionReceipt) {
|
||||
console.log(transactionReceipt);
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
Ethereum Name Resolution
|
||||
========================
|
||||
|
||||
The Ethereum Naming Service (ENS) allows easy to remember and use names to be
|
||||
assigned to Ethereum addresses. Any provider operation which takes an address
|
||||
may also take an ENS name.
|
||||
|
||||
It is often useful to resolve a name entered by a user or perform a reverse lookup
|
||||
of an address to get a more human readbale name.
|
||||
|
||||
**Resolving Names**\ ::
|
||||
|
||||
var providers = require('ethers').providers;
|
||||
var provider = providers.getDefaultProvider();
|
||||
provider.resolveName('registrar.firefly.eth').then(function(address) {
|
||||
console.log(address);
|
||||
// '0x6fC21092DA55B392b045eD78F4732bff3C580e2c'
|
||||
});
|
||||
|
||||
**Looking up Addresses**\ ::
|
||||
|
||||
provider.lookupAddress('0x6fC21092DA55B392b045eD78F4732bff3C580e2c').then(function(name) {
|
||||
console.log(name);
|
||||
// 'registrar.firefly.eth'
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
Contract Execution
|
||||
==================
|
||||
|
||||
These are relatively low-level calls. The :ref:`Contracts API <api-contract>` should
|
||||
usually be used instead.
|
||||
|
||||
:sup:`prototype` . call ( transaction )
|
||||
Send the **read-only** (constant) *transaction* to a single Ethereum node and
|
||||
return a :ref:`Promise <promise>` with the result (as a :ref:`hex string <hexstring>`) of executing it.
|
||||
(See :ref:`Transaction Requests <transactionrequest>`)
|
||||
|
||||
This is free, since it does not change any state on the blockchain.
|
||||
|
||||
:sup:`prototype` . estimateGas ( transaction )
|
||||
Send a *transaction* to a single Ethereum node and return a :ref:`Promise <promise>` with the
|
||||
estimated amount of gas required (as a :ref:`BigNumber <bignumber>`) to send it.
|
||||
(See :ref:`Transaction Requests <transactionrequest>`)
|
||||
|
||||
This is free, but only an estimate. Providing too little gas will result in a
|
||||
transaction being rejected (while still consuming all provided gas).
|
||||
|
||||
:sup:`prototype` . sendTransaction ( signedTransaction )
|
||||
Send the *signedTransaction* to the **entire** Ethereum network and returns a :ref:`Promise <promise>`
|
||||
with the transaction hash.
|
||||
|
||||
**This will consume gas** from the account that signed the transaction.
|
||||
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
**Call (Constant) Functions** ::
|
||||
|
||||
var ethers = require('ethers');
|
||||
var provider = ethers.providers.getDefaultProvider();
|
||||
|
||||
// setup a transaction to call the CryptoKitties.symbol() function
|
||||
// CryptoKitties contract address
|
||||
var address = "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d";
|
||||
// first 8 nibbles of the hash of symbol()
|
||||
var data = ethers.utils.id('symbol()').substring(0,10);
|
||||
var transaction = {
|
||||
to: address,
|
||||
data: data
|
||||
}
|
||||
|
||||
provider.call(transaction).then(function(result) {
|
||||
console.log(result);
|
||||
// '0x000000000000000000000000000000000000000000000000000000000000002'+
|
||||
// '00000000000000000000000000000000000000000000000000000000000000002'+
|
||||
// '434b000000000000000000000000000000000000000000000000000000000000'
|
||||
});
|
||||
|
||||
**sendTransaction** ::
|
||||
|
||||
var ethers = require('ethers');
|
||||
var privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
|
||||
var wallet = new ethers.Wallet(privateKey);
|
||||
wallet.provider = ethers.providers.getDefaultProvider('ropsten');
|
||||
|
||||
var transaction = {
|
||||
to: "0x88a5C2d9919e46F883EB62F7b8Dd9d0CC45bc290",
|
||||
value: ethers.utils.parseEther("0.1")
|
||||
};
|
||||
|
||||
var estimateGasPromise = wallet.estimateGas(transaction);
|
||||
|
||||
estimateGasPromise.then(function(gasEstimate) {
|
||||
console.log(gasEstimate.toString());
|
||||
transaction.gasLimit = gasEstimate;
|
||||
|
||||
|
||||
// Send the transaction
|
||||
var sendTransactionPromise = wallet.sendTransaction(transaction);
|
||||
|
||||
sendTransactionPromise.then(function(transactionHash) {
|
||||
console.log(transactionHash);
|
||||
});
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
Contract State
|
||||
==============
|
||||
|
||||
:sup:`prototype` . getCode ( addressOrName )
|
||||
Returns a :ref:`Promise <promise>` with the bytecode (as a :ref:`hex string <hexstring>`)
|
||||
at *addressOrName*.
|
||||
|
||||
:sup:`prototype` . getStorageAt ( addressOrName , position [ , blockTag ] )
|
||||
Returns a :ref:`Promise <promise>` with the value (as a :ref:`hex string <hexstring>`) at
|
||||
*addressOrName* in *position* at *blockTag*. (See :ref:`Block Tags <blocktag>`)
|
||||
|
||||
default: *blockTag*\ = "latest"
|
||||
|
||||
:sup:`prototype` . getLogs ( filter )
|
||||
Returns a :ref:`Promise <promise>` with an array (possibly empty) of the logs that
|
||||
match the *filter*. (See :ref:`Filters <filter>`)
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
**getCode** ::
|
||||
|
||||
var ethers = require('ethers');
|
||||
var provider = ethers.providers.getDefaultProvider();
|
||||
|
||||
var contractAddress = '0x6fC21092DA55B392b045eD78F4732bff3C580e2c';
|
||||
var contractEnsName = 'registrar.firefly.eth';
|
||||
var codePromise = provider.getCode(contractEnsName);
|
||||
codePromise.then(function(result){
|
||||
console.log('getCode by ENS name:');
|
||||
console.log(result);
|
||||
});
|
||||
|
||||
var codeByAddressPromise = provider.getCode(contractAddress);
|
||||
codeByAddressPromise.then(function(result){
|
||||
console.log('getCode by contract address:');
|
||||
console.log(result);
|
||||
});
|
||||
|
||||
|
||||
**getStorageAt** ::
|
||||
|
||||
|
||||
var ethers = require('ethers');
|
||||
var provider = ethers.providers.getDefaultProvider();
|
||||
|
||||
var contractEnsName = 'registrar.firefly.eth';
|
||||
var storagePromise = provider.getStorageAt(contractEnsName, 0);
|
||||
storagePromise.then(function(result){
|
||||
console.log(result);
|
||||
});
|
||||
|
||||
**getLogs** ::
|
||||
|
||||
|
||||
var ethers = require('ethers');
|
||||
var provider = ethers.providers.getDefaultProvider();
|
||||
|
||||
var cryptoKittiesContractAddress = '0x06012c8cf97BEaD5deAe237070F9587f8E7A266d';
|
||||
var topic = '0x241ea03ca20251805084d27d4440371c34a0b85ff108f6bb5611248f73818b80';
|
||||
var filter = {
|
||||
fromBlock: 5044502,
|
||||
address: cryptoKittiesContractAddress,
|
||||
topics: [ topic ]
|
||||
}
|
||||
var filterPromise = provider.getLogs(filter);
|
||||
filterPromise.then(function(result){
|
||||
console.log(result);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Events
|
||||
======
|
||||
|
||||
These methods allow management of callbacks on certain events on the blockchain
|
||||
and contracts. They are largely based on the `EventEmitter API`_.
|
||||
|
||||
:sup:`prototype` . on ( eventType , callback )
|
||||
Register a callback for any future *eventType*; see below for callback parameters
|
||||
|
||||
:sup:`prototype` . once ( eventType , callback)
|
||||
Register a callback for the next (and only next) *eventType*; see below for callback parameters
|
||||
|
||||
:sup:`prototype` . removeListener ( eventType , callback )
|
||||
Unregister the *callback* for *eventType*; if the same callback is registered
|
||||
more than once, only the first registered instance is removed
|
||||
|
||||
:sup:`prototype` . removeAllListeners ( eventType )
|
||||
Unregister all callbacks for *eventType*
|
||||
|
||||
:sup:`prototype` . listenerCount ( [ eventType ] )
|
||||
Return the number of callbacks registered for *eventType*, or if ommitted, the
|
||||
total number of callbacks registered
|
||||
|
||||
:sup:`prototype` . resetEventsBlock ( blockNumber )
|
||||
Begin scanning for events from *blockNumber*. By default, events begin at the
|
||||
block number that the provider began polling at.
|
||||
|
||||
Event Types
|
||||
-----------
|
||||
|
||||
"block"
|
||||
Whenever a new block is mined
|
||||
|
||||
``callback( blockNumber )``
|
||||
|
||||
any address
|
||||
When the balance of the corresponding address changes
|
||||
|
||||
``callback( balance )``
|
||||
|
||||
any transaction hash
|
||||
When the corresponding transaction is mined; also see
|
||||
:ref:`Waiting for Transactions <waitForTransaction>`
|
||||
|
||||
``callback( transaction )``
|
||||
|
||||
an array of topics
|
||||
When any of the topics are triggered in a block's logs; when using the
|
||||
:ref:`Contract API <api-contract>`, this is automatically handled;
|
||||
|
||||
``callback( log )``
|
||||
|
||||
.. _waitForTransaction:
|
||||
|
||||
Waiting for Transactions
|
||||
------------------------
|
||||
|
||||
:sup:`prototype` . waitForTransaction ( transactionHash [ , timeout ] )
|
||||
Return a :ref:`Promise <promise>` which returns the transaction once *transactionHash* is
|
||||
mined, with an optional *timeout* (in milliseconds)
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
// Get notified on every new block
|
||||
provider.on('block', function(blockNumber) {
|
||||
console.log('New Block: ' + blockNumber);
|
||||
});
|
||||
|
||||
// Get notified on account balance change
|
||||
provider.on('0x46Fa84b9355dB0708b6A57cd6ac222950478Be1d', function(balance) {
|
||||
console.log('New Balance: ' + balance);
|
||||
});
|
||||
|
||||
// Get notified when a transaction is mined
|
||||
provider.once(transactionHash, function(transaction) {
|
||||
console.log('Transaction Minded: ' + transaction.hash);
|
||||
console.log(transaction);
|
||||
);
|
||||
|
||||
// OR equivalently the waitForTransaction() returns a Promise
|
||||
|
||||
provider.waitForTransaction(transactionHash).then(function(transaction) {
|
||||
console.log('Transaction Mined: ' + transaction.hash);
|
||||
console.log(transaction);
|
||||
});
|
||||
|
||||
|
||||
// Get notified when a contract event is logged
|
||||
var eventTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';
|
||||
provider.on([ eventTopic ], function(log) {
|
||||
console.log('Event Log');
|
||||
console.log(log);
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
Objects
|
||||
=======
|
||||
|
||||
There are several common objects and types that are commonly used as input parameters or
|
||||
return types for various provider calls.
|
||||
|
||||
.. _blocktag:
|
||||
|
||||
Block Tag
|
||||
---------
|
||||
|
||||
A block tag is used to uniquely identify a block's position in the blockchain:
|
||||
|
||||
a Number or :ref:`hex string <hexstring>`:
|
||||
Each block has a block number (eg. ``42`` or ``"0x2a``.
|
||||
|
||||
"latest":
|
||||
The most recently mined block.
|
||||
|
||||
"pending":
|
||||
The block that is currently being mined.
|
||||
|
||||
.. _blockresponse:
|
||||
|
||||
Block Responses
|
||||
---------------
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
parentHash: "0x3d8182d27303d92a2c9efd294a36dac878e1a9f7cb0964fa0f789fa96b5d0667",
|
||||
hash: "0x7f20ef60e9f91896b7ebb0962a18b8defb5e9074e62e1b6cde992648fe78794b",
|
||||
number: 3346463,
|
||||
|
||||
difficulty: 183765779077962,
|
||||
timestamp: 1489440489,
|
||||
nonce: "0x17060cb000d2c714",
|
||||
extraData: "0x65746865726d696e65202d20555331",
|
||||
|
||||
gasLimit: utils.bigNumberify("3993225"),
|
||||
gasUsed: utils.bigNuberify("3254236"),
|
||||
|
||||
miner: "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
|
||||
transactions: [
|
||||
"0x125d2b846de85c4c74eafb6f1b49fdb2326e22400ae223d96a8a0b26ccb2a513",
|
||||
"0x948d6e8f6f8a4d30c0bd527becbe24d15b1aba796f9a9a09a758b622145fd963",
|
||||
... [ 49 more transaction hashes ] ...
|
||||
"0xbd141969b164ed70388f95d780864210e045e7db83e71f171ab851b2fba6b730"
|
||||
]
|
||||
}
|
||||
|
||||
.. _transactionrequest:
|
||||
|
||||
Transaction Requests
|
||||
--------------------
|
||||
|
||||
Any property which accepts a number may also be specified as a :ref:`BigNumber <bignumber>`
|
||||
or :ref:`hex string <hexstring>`.
|
||||
|
||||
::
|
||||
|
||||
// Example:
|
||||
{
|
||||
// Required unless deploying a contract (in which case omit)
|
||||
to: addressOrName, // the target address or ENS name
|
||||
|
||||
// These are optional/meaningless for call and estimateGas
|
||||
nonce: 0, // the transaction nonce
|
||||
gasLimit: 0, // the maximum gas this transaction may spend
|
||||
gasPrice: 0, // the price (in wei) per unit of gas
|
||||
|
||||
// These are always optional (but for call, data is usually specified)
|
||||
data: "0x", // extra data for the transaction, or input for call
|
||||
value: 0, // the amount (in wei) this transaction is sending
|
||||
chainId: 3 // the network ID; usually added by a signer
|
||||
}
|
||||
|
||||
|
||||
.. _transactionresponse:
|
||||
|
||||
Transaction Response
|
||||
--------------------
|
||||
|
||||
::
|
||||
|
||||
// Example:
|
||||
{
|
||||
// Only available for mined transactions
|
||||
blockHash: "0x7f20ef60e9f91896b7ebb0962a18b8defb5e9074e62e1b6cde992648fe78794b",
|
||||
blockNumber: 3346463,
|
||||
transactionIndex: 51,
|
||||
|
||||
// Exactly one of these will be present (send vs. deploy contract)
|
||||
creates: null,
|
||||
to: "0xc149Be1bcDFa69a94384b46A1F91350E5f81c1AB",
|
||||
|
||||
// The transaction hash
|
||||
hash: "0xf517872f3c466c2e1520e35ad943d833fdca5a6739cfea9e686c4c1b3ab1022e",
|
||||
|
||||
// See above (Transaction Requests) for these explained
|
||||
data: "0x",
|
||||
from: "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
|
||||
gasLimit: utils.bigNumberify("90000"),
|
||||
gasPrice: utils.bigNumberify("21488430592"),
|
||||
nonce: 0,
|
||||
value: utils.parseEther(1.0017071732629267),
|
||||
|
||||
// The network ID (or chain ID); 0 indicates replay-attack vulnerable
|
||||
// (eg. 1 = Homestead mainnet, 3 = Ropsten testnet)
|
||||
networkId: 1,
|
||||
|
||||
// The signature of the transaction
|
||||
r: "0x5b13ef45ce3faf69d1f40f9d15b0070cc9e2c92f3df79ad46d5b3226d7f3d1e8",
|
||||
s: "0x535236e497c59e3fba93b78e124305c7c9b20db0f8531b015066725e4bb31de6",
|
||||
v: 37,
|
||||
|
||||
// The raw transaction
|
||||
raw: "0xf87083154262850500cf6e0083015f9094c149be1bcdfa69a94384b46a1f913" +
|
||||
"50e5f81c1ab880de6c75de74c236c8025a05b13ef45ce3faf69d1f40f9d15b0" +
|
||||
"070cc9e2c92f3df79ad46d5b3226d7f3d1e8a0535236e497c59e3fba93b78e1" +
|
||||
"24305c7c9b20db0f8531b015066725e4bb31de6"
|
||||
}
|
||||
|
||||
.. _transactionReceipt:
|
||||
|
||||
Transaction Receipts
|
||||
--------------------
|
||||
|
||||
::
|
||||
|
||||
// Example
|
||||
{
|
||||
transactionHash: "0x7dec07531aae8178e9d0b0abbd317ac3bb6e8e0fd37c2733b4e0d382ba34c5d2",
|
||||
|
||||
// The block this transaction was mined into
|
||||
blockHash: "0xca1d4d9c4ac0b903a64cf3ae3be55cc31f25f81bf29933dd23c13e51c3711840",
|
||||
blockNumber: 3346629,
|
||||
|
||||
// The index into this block of the transaction
|
||||
transactionIndex: 1,
|
||||
|
||||
// The address of the contract (if one was created)
|
||||
contractAddress: null,
|
||||
|
||||
// Gas
|
||||
cumulativeGasUsed: utils.bigNumberify("42000"),
|
||||
gasUsed: utils.bigNumberify("21000"),
|
||||
|
||||
// Logs
|
||||
log: [ ],
|
||||
logsBloom: "0x00" ... [ 256 bytes of 0 ] ... "00",
|
||||
|
||||
// Post-Byzantium hard-fork
|
||||
byzantium: false
|
||||
|
||||
////////////
|
||||
// Pre-byzantium blocks will have a state root:
|
||||
root: "0x8a27e1f7d3e92ae1a01db5cce3e4718e04954a34e9b17c1942011a5f3a942bf4",
|
||||
|
||||
////////////
|
||||
// Post-byzantium blocks will have a status (0 indicated failure during execution)
|
||||
// status: 1
|
||||
}
|
||||
|
||||
.. _filter:
|
||||
|
||||
Filters
|
||||
-------
|
||||
|
||||
Filtering on topics supports a `somewhat complicated`_ specification, however,
|
||||
for the vast majority of filters, a single topic is usually sufficient (see the example below).
|
||||
|
||||
The *EtherscanProvider* only supports a single topic.
|
||||
|
||||
::
|
||||
|
||||
// Example
|
||||
{
|
||||
// Optional; The range of blocks to limit querying (See: Block Tags above)
|
||||
fromBlock: "latest",
|
||||
toBlock: "latest",
|
||||
|
||||
// Optional; An address (or ENS name) to filter by
|
||||
address: addressOrName,
|
||||
|
||||
// Optional; A (possibly nested) list of topics
|
||||
topics: [ topic1 ]
|
||||
}
|
||||
|
||||
-----
|
||||
|
||||
Provider Specific Extra API Calls
|
||||
=================================
|
||||
|
||||
Etherscan
|
||||
---------
|
||||
|
||||
:sup:`prototype` . getEtherPrice ( )
|
||||
Returns a :ref:`Promise <promise>` with the price of ether in USD.
|
||||
|
||||
:sup:`prototype` . getHistory ( addressOrName [ , startBlock [ , endBlock ] ] )
|
||||
Returns a :ref:`Promise <promise>` with an array of :ref:`Transaction Responses <transactionresponse>`
|
||||
for each transaction to or from *addressOrName* between *startBlock* and *endBlock* (inclusive).
|
||||
|
||||
**Examples**
|
||||
|
||||
::
|
||||
|
||||
var provider = new ethers.providers.EtherscanProvider();
|
||||
|
||||
// Getting the current Ethereum price
|
||||
provider.getEtherPrice().then(function(price) {
|
||||
console.log("Ether price in USD: " + price);
|
||||
});
|
||||
|
||||
|
||||
// Getting the transaction history of an address
|
||||
var address = '0xb2682160c482eB985EC9F3e364eEc0a904C44C23';
|
||||
var startBlock = 3135808;
|
||||
var endBlock = 5091477;
|
||||
provider.getHistory(address, startBlock, endBlock).then(function(history) {
|
||||
console.log(history);
|
||||
// [
|
||||
// {
|
||||
// hash: '0x327632ccb6d7bb47b455383e936b2f14e6dc50dbefdc214870b446603b468675',
|
||||
// blockHash: '0x0415f0d2741de45fb748166c7dc2aad9b3ff66bcf7d0a127f42a71d3e286c36d',
|
||||
// blockNumber: 3135808,
|
||||
// transactionIndex: 1,
|
||||
// from: '0xb2682160c482eB985EC9F3e364eEc0a904C44C23',
|
||||
// gasPrice: ethers.utils.bigNumberify('0x4a817c800'),
|
||||
// gasLimit: ethers.utils.bigNumberify('0x493e0'),
|
||||
// to: '0xAe572713CfE65cd7033774170F029B7219Ee7f70',
|
||||
// value: ethers.utils.bigNumberify('0xd2f13f7789f0000'),
|
||||
// nonce: 25,
|
||||
// data: '0x',
|
||||
// creates: null,
|
||||
// networkId: 0
|
||||
// },
|
||||
// {
|
||||
// hash: '0x7c10f2e7125a1fa5e37b54f5fac5465e8d594f89ff97916806ca56a5744812d9',
|
||||
// ...
|
||||
// }
|
||||
// ]
|
||||
});
|
||||
|
||||
|
||||
Web3Provider
|
||||
------------
|
||||
|
||||
:sup:`prototype` . listAccounts ( )
|
||||
Returns a :ref:`Promise <promise>` with a list of all accounts the node connected
|
||||
to this Web3 controls.
|
||||
|
||||
:sup:`prototype` . getSigner( [ address ] )
|
||||
Returns a :ref:`Signer <custom-signer>` that uses an account on the node
|
||||
the Web3 object is connected to. If no address is specified, the first
|
||||
account on the node is used.
|
||||
|
||||
|
||||
**Examples**
|
||||
|
||||
::
|
||||
|
||||
web3Provider.listAccounts().then(function(accounts) {
|
||||
var signer = web3Provider.getSigner(accounts[1]);
|
||||
console.log(signer);
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
.. _Etherscan: https://etherscan.io/apis
|
||||
.. _web service API: https://etherscan.io/apis
|
||||
.. _INFURA: https://infura.io
|
||||
.. _Parity: https://ethcore.io/parity.html
|
||||
.. _Geth: https://geth.ethereum.org
|
||||
.. _JSON-RPC API: https://github.com/ethereum/wiki/wiki/JSON-RPC
|
||||
.. _EventEmitter API: https://nodejs.org/dist/latest-v6.x/docs/api/events.html
|
||||
.. _replay attacks: https://github.com/ethereum/EIPs/issues/155
|
||||
.. _somewhat complicated: https://github.com/ethereum/wiki/wiki/JSON-RPC#a-note-on-specifying-topic-filters
|
||||
.. _HTTPProvider: https://github.com/ethereum/web3.js/blob/develop/lib/web3/httpprovider.js
|
||||
.. _IPCProvider: https://github.com/ethereum/web3.js/blob/develop/lib/web3/ipcprovider.js
|
||||
|
||||
.. EOF
|
@ -1,440 +0,0 @@
|
||||
Utilities
|
||||
*********
|
||||
|
||||
The utility functions exposed in both the *ethers* umbrella package and the *ethers-utils*::
|
||||
|
||||
var utils = require('ethers').utils;
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _bignumber:
|
||||
|
||||
Big Numbers
|
||||
===========
|
||||
|
||||
A BigNumber is an immutable object which allow math operations to be carried
|
||||
out on numbers far larger than :ref:`JavaScript can accurately handle <ieee754>`.
|
||||
Many functions return these, so it is important to understand how to work with these.
|
||||
|
||||
:sup:`prototype` . add ( otherValue )
|
||||
Return a new BigNumber of this plus *otherValue*
|
||||
|
||||
:sup:`prototype` . sub ( otherValue )
|
||||
Return a new BigNumber of this minus *otherValue*
|
||||
|
||||
:sup:`prototype` . mul ( otherValue )
|
||||
Return a new BigNumber of this times *otherValue*
|
||||
|
||||
:sup:`prototype` . div ( otherValue )
|
||||
Return a new BigNumber of this divided by *otherValue*
|
||||
|
||||
:sup:`prototype` . mod ( otherValue )
|
||||
Return a new BigNumber of this modulo *otherValue*
|
||||
|
||||
:sup:`prototype` . eq ( otherValue )
|
||||
Return true if this is equal to *otherValue*
|
||||
|
||||
:sup:`prototype` . lt ( otherValue )
|
||||
Return true if this is less than *otherValue*
|
||||
|
||||
:sup:`prototype` . lte ( otherValue )
|
||||
Return true if this is less or equal to *otherValue*
|
||||
|
||||
:sup:`prototype` . gt ( otherValue )
|
||||
Return true if this is greater than *otherValue*
|
||||
|
||||
:sup:`prototype` . gte ( otherValue )
|
||||
Return true if this is greater than or equal to *otherValue*
|
||||
|
||||
:sup:`prototype` . isZero ( )
|
||||
Return true if this is equal to zero
|
||||
|
||||
:sup:`prototype` . toNumber ( )
|
||||
Return a JavaScript number representation; an error is thrown if the value is
|
||||
outside the safe range for JavaScript IEEE 754 64-bit floating point numbers
|
||||
|
||||
:sup:`prototype` . toString ()
|
||||
Return a decimal string representation
|
||||
|
||||
:sup:`prototype` . toHexString ( )
|
||||
Return a **0x prefixed** hexidecimal representation
|
||||
|
||||
|
||||
Creating Instances
|
||||
------------------
|
||||
|
||||
:sup:`utils` **. bigNumberify** ( value )
|
||||
Returns a BigNumber instance of *value*. The *value* may be anything which can be
|
||||
reliably converted into a BigNumber:
|
||||
|
||||
*Decimal String*
|
||||
A string consisting of the decimal digits 0 through 9, optionally with a leading
|
||||
negative sign.
|
||||
|
||||
**examples:** utils.bigNumberify("42")
|
||||
|
||||
*Hex String*
|
||||
A :ref:`hex string <hexstring>`, witch has aa **prefix of 0x** and consisting
|
||||
of the hexidecimal digits 0 through 9 and a through f, case-insensitive. Must
|
||||
be non-negative.
|
||||
|
||||
**examples:** utils.bigNumberify("0x2a")
|
||||
|
||||
*JavaScript Numbers*
|
||||
Numbers must be within the `safe range`_ for JavaScript.
|
||||
|
||||
**examples:** utils.bigNumberify(42)
|
||||
|
||||
*Arrayish*
|
||||
Treats the :ref:`arrayish <api-arrayish>` as a big-endian encoded bytes representation.
|
||||
|
||||
**examples:** utils.bigNumberify([ 42 ])
|
||||
|
||||
*BigNumber*
|
||||
Returns *value*, since a BigNumber is immutable.
|
||||
|
||||
.. _safe range: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
var utils = require('ethers').utils;
|
||||
var gasPriceWei = utils.bigNumberify("20902747399");
|
||||
var gasLimit = utils.bigNumberify(3000000);
|
||||
|
||||
var maxCostWei = gasPriceWei.mul(gasLimit)
|
||||
console.log("Max Cost: " + maxCostWei.toString());
|
||||
// "Max Cost: 62708242197000000"
|
||||
|
||||
console.log("Number: " + maxCostWei.toNumber());
|
||||
// throws an Error, the value is too large for JavaScript to handle safely
|
||||
|
||||
-----
|
||||
|
||||
|
||||
Ether Strings and Wei
|
||||
=====================
|
||||
|
||||
:sup:`utils` . etherSymbol
|
||||
The ethereum symbol (the Greek letter *Xi* )
|
||||
|
||||
.. _parseEther:
|
||||
|
||||
:sup:`utils` . parseEther ( etherString )
|
||||
Parse the *etherString* representation of ether into a BigNumber instance
|
||||
of the amount of wei.
|
||||
|
||||
.. _formatEther:
|
||||
|
||||
:sup:`utils` . formatEther ( wei [ , options ] )
|
||||
Format an amount of *wei* into a decimal string representing the amount of ether. The
|
||||
*options* object supports the keys ``commify`` and ``pad``. The output will always
|
||||
include at least one whole number and at least one decimal place.
|
||||
|
||||
.. _parseUnits:
|
||||
|
||||
:sup:`utils` . parseUnits ( valueString , decimalsOrUnitName )
|
||||
Parse the *valueString* representation of units into a BigNumber instance
|
||||
of the amount of wei. The *decimalsOrUnitsName* may be a number of decimals between
|
||||
3 and 18 (multiple of 3) or a name, such as `gwei`.
|
||||
|
||||
.. _formatUnits:
|
||||
|
||||
:sup:`utils` . formatUnits ( wei , decimalsOrUnitName [ , options ] )
|
||||
Format an amount of *wei* into a decimal string representing the amount of units. The
|
||||
*options* object supports the keys ``commify`` and ``pad``. The output will always
|
||||
include at least one whole number and at least one decimal place. The *decimalsOrUnitsName*
|
||||
may be a number of decimals between 3 and 18 (multiple of 3) or a name, such as `gwei`.
|
||||
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
var utils = require('ethers').utils;
|
||||
var wei = utils.parseEther('1000.0');
|
||||
console.log(wei.toString(10));
|
||||
// "1000000000000000000000"
|
||||
|
||||
console.log(utils.formatEther(0));
|
||||
// "0.0"
|
||||
|
||||
var wei = utils.bigNumberify("1000000000000000000000");
|
||||
|
||||
console.log(utils.formatEther(wei));
|
||||
// "1000.0"
|
||||
|
||||
console.log(utils.formatEther(wei, {commify: true}));
|
||||
// "1,000.0"
|
||||
|
||||
console.log(utils.formatEther(wei, {pad: true}));
|
||||
// "1000.000000000000000000" (18 decimal places)
|
||||
|
||||
console.log(utils.formatEther(wei, {commify: true, pad: true}));
|
||||
// "1,000.000000000000000000" (18 decimal places)
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Addresses
|
||||
=========
|
||||
|
||||
There are :ref:`several formats <checksum-address>` available on the Ethereum network for
|
||||
addresses, and it is often useful to be able to convert between them.
|
||||
|
||||
|
||||
.. _api-getAddress:
|
||||
|
||||
:sup:`utils` . getAddress ( address [ , generateIcap ] )
|
||||
Normalize an address to a :ref:`checksum address <checksum-address>`, or as an
|
||||
:ref:`ICAP <icap-address>` address if *generateIcap* is true.
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
var utils = require('ethers').utils;
|
||||
var address = "0xd115bffabbdd893a6f7cea402e7338643ced44a6";
|
||||
var icapAddress = "XE93OF8SR0OWI6F4FO88KWO4UNNGG1FEBHI";
|
||||
|
||||
console.log(utils.getAddress(address));
|
||||
// "0xD115BFFAbbdd893A6f7ceA402e7338643Ced44a6"
|
||||
|
||||
console.log(utils.getAddress(icapAddress));
|
||||
// "0xD115BFFAbbdd893A6f7ceA402e7338643Ced44a6"
|
||||
|
||||
console.log(utils.getAddress(address, true));
|
||||
// "XE93OF8SR0OWI6F4FO88KWO4UNNGG1FEBHI"
|
||||
|
||||
console.log(utils.getAddress(icapAddress, true));
|
||||
// "XE93OF8SR0OWI6F4FO88KWO4UNNGG1FEBHI"
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _api-utf8-strings:
|
||||
|
||||
UTF-8 Strings
|
||||
=============
|
||||
|
||||
.. _api-utf8-to-bytes:
|
||||
|
||||
:sup:`utils` . toUtf8Bytes ( string )
|
||||
Converts a UTF-8 string to a Uint8Array.
|
||||
|
||||
.. _api-utf8-to-string:
|
||||
|
||||
:sup:`utils` . toUtf8String ( hexStringOrArrayish )
|
||||
Converts a hex-encoded string or array to its UTF-8 representation.
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
var utils = require('ethers').utils;
|
||||
var text = "Hello World";
|
||||
|
||||
var bytes = utils.toUtf8Bytes(text);
|
||||
console.log(bytes);
|
||||
// Uint8Array [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
|
||||
|
||||
console.log(utils.toUtf8String(bytes));
|
||||
// "Hello World"
|
||||
|
||||
var hexString = "0x48656c6c6f20576f726c64";
|
||||
console.log(utils.toUtf8String(hexString));
|
||||
// "Hello World"
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _api-utils-crypto:
|
||||
|
||||
Cryptographic Functions
|
||||
=======================
|
||||
|
||||
:sup:`utils` . keccak256 ( hexStringOrArrayish )
|
||||
Compute the keccak256 cryptographic hash of a value, returned as a hex string. (Note:
|
||||
often Ethereum refers to this, **incorrectly**, as SHA3)
|
||||
|
||||
:sup:`utils` . id ( utf8String )
|
||||
Compute the keccak256 cryptographic hash of a UTF-8 string, returned as a hex string.
|
||||
|
||||
:sup:`utils` . sha256 ( hexStringOrArrayish )
|
||||
Compute the SHA2-256 cryptographic hash of a value, returned as a hex string.
|
||||
|
||||
:sup:`utils` . randomBytes ( length )
|
||||
Return a Uint8Array of cryptographically secure random bytes
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
**Hashing Binary Data** ::
|
||||
|
||||
var utils = require('ethers').utils;
|
||||
console.log(utils.keccak256([ 0x42 ]));
|
||||
// '0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111'
|
||||
|
||||
console.log(utils.keccak256("0x42"));
|
||||
// '0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111'
|
||||
|
||||
|
||||
console.log(utils.sha256([ 0x42 ]));
|
||||
// '0xdf7e70e5021544f4834bbee64a9e3789febc4be81470df629cad6ddb03320a5c'
|
||||
|
||||
console.log(utils.sha256("0x42"));
|
||||
// '0xdf7e70e5021544f4834bbee64a9e3789febc4be81470df629cad6ddb03320a5c'
|
||||
|
||||
|
||||
**Hashing UTF-8 Strings** ::
|
||||
|
||||
var utils = require('ethers').utils;
|
||||
// Convert the string to binary data
|
||||
var utf8Bytes = utils.toUtf8Bytes('Hello World');
|
||||
|
||||
console.log(utils.keccak256(utf8Bytes));
|
||||
// '0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba'
|
||||
|
||||
console.log(utils.sha256(utf8Bytes));
|
||||
// '0xa591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e'
|
||||
|
||||
|
||||
console.log(utils.id("Hello World"));
|
||||
// '0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba'
|
||||
|
||||
console.log(utils.id("addr(bytes32)"));
|
||||
// '0x3b3b57de213591bb50e06975ea011e4c8c4b3e6de4009450c1a9e55f66e4bfa4'
|
||||
|
||||
|
||||
**Random Bytes** ::
|
||||
|
||||
var utils = require('ethers').utils;
|
||||
console.log(utils.randomBytes(3));
|
||||
// Uint8Array [ 194, 22, 140 ]
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Solidity Cryptographic Functions
|
||||
================================
|
||||
|
||||
Solidity uses a `non-standard packed mode`_ to encode parameters that are passed
|
||||
into its hashing functions. The parameter types and values can be used to compute
|
||||
the result of the hash functions as would be performed by Solidity.
|
||||
|
||||
:sup:`utils` . solidityKeccak256 ( types, values )
|
||||
Compute the keccak256 cryptographic hash using the Solidity non-standard (tightly)
|
||||
packed data for *values* given the *types*.
|
||||
|
||||
:sup:`utils` . soliditySha256 ( types, values )
|
||||
Compute the SHA256 cryptographic hash using the Solidity non-standard (tightly)
|
||||
packed data for *values* given the *types*.
|
||||
|
||||
:sup:`utils` . solidityPack ( types, values )
|
||||
Compute the Solidity non-standard (tightly) packed data for *values* given the *types*.
|
||||
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
var utils = require('ethers').utils;
|
||||
var result = utils.solidityKeccak256([ 'int8', 'bytes1', 'string' ], [ -1, '0x42', 'hello' ]);
|
||||
console.log(result);
|
||||
// '0x52d7e6a62ca667228365be2143375d0a2a92a3bd4325dd571609dfdc7026686e'
|
||||
|
||||
result = utils.soliditySha256([ 'int8', 'bytes1', 'string' ], [ -1, '0x42', 'hello' ]);
|
||||
console.log(result);
|
||||
// '0x1eaebba7999af2691d823bf0c817e635bbe7e89ec7ed32a11e00ca94e86cbf37'
|
||||
|
||||
result = utils.solidityPack([ 'int8', 'bytes1', 'string' ], [ -1, '0x42', 'hello' ]);
|
||||
console.log(result);
|
||||
// '0xff4268656c6c6f'
|
||||
|
||||
-----
|
||||
|
||||
.. _api-arrayish:
|
||||
|
||||
Arrayish
|
||||
========
|
||||
|
||||
An arrayish object is any such that it:
|
||||
|
||||
* has a *length* property
|
||||
* has a value for each index from 0 up to (but excluding) *length*
|
||||
* has a valid byte for each value; a byte is an integer in the range [0, 255]
|
||||
* is **NOT** a string
|
||||
|
||||
:sup:`utils` . isArrayish ( object )
|
||||
Returns true if *object* can be treated as an arrayish object.
|
||||
|
||||
:sup:`utils` . arrayify ( hexStringOrBigNumberOrArrayish )
|
||||
Returns a Uint8Array of a hex string, BigNumber or of an `Arrayish`_ object.
|
||||
|
||||
:sup:`utils` . concat ( arrayOfHexStringsAndArrayish )
|
||||
Return a Uint8Array of all *arrayOfHexStringsAndArrayish* concatenated.
|
||||
|
||||
:sup:`utils` . padZeros ( typedUint8Array, length )
|
||||
Return a Uint8Array of *typedUint8Array* with zeros prepended to *length* bytes.
|
||||
|
||||
:sup:`utils` . stripZeros ( hexStringOrArrayish )
|
||||
Returns a Uint8Array with all leading zero **bytes** striped.
|
||||
|
||||
-----
|
||||
|
||||
.. _hexstring:
|
||||
|
||||
Hex Strings
|
||||
===========
|
||||
|
||||
A hex string is **always** prefixed with "0x" and consists of the characters
|
||||
0 -- 9 and a -- f. It is always returned lower case with even-length, but any hex
|
||||
string passed into a function may be any case and may be odd-length.
|
||||
|
||||
|
||||
:sup:`utils` . hexlify ( numberOrBigNumberOrHexStringOrArrayish )
|
||||
Converts any number, :ref:`BigNumber <bignumber>`, hex string or
|
||||
`Arrayish`_ to a hex string. (otherwise, throws an error)
|
||||
|
||||
-----
|
||||
|
||||
Contract Addresses
|
||||
==================
|
||||
|
||||
Every contract deployed on the Ethereum network requires an address (you can think
|
||||
of this as the memory address which the running application lives at). The address
|
||||
is generated from a cryptographic hash of the address of the creator and the nonce
|
||||
of the transaction.
|
||||
|
||||
:sup:`utils` . getContractAddress ( transaction )
|
||||
Computes the contract address a contract would have if this transaction
|
||||
created a contract. (transaction requires only ``from`` and ``nonce`` be
|
||||
defined)
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
var utils = require('ethers').utils;
|
||||
// Ropsten: 0x5bdfd14fcc917abc2f02a30721d152a6f147f09e8cbaad4e0d5405d646c5c3e1
|
||||
var transaction = {
|
||||
from: '0xc6af6e1a78a6752c7f8cd63877eb789a2adb776c',
|
||||
nonce: 0
|
||||
};
|
||||
|
||||
console.log(utils.getContractAddress(transaction));
|
||||
// "0x0CcCC7507aEDf9FEaF8C8D731421746e16b4d39D"
|
||||
|
||||
-----
|
||||
|
||||
.. _non-standard packed mode: http://solidity.readthedocs.io/en/develop/abi-spec.html#non-standard-packed-mode
|
||||
|
||||
.. EOF
|
@ -1,405 +0,0 @@
|
||||
.. _api-wallet:
|
||||
|
||||
Wallets
|
||||
*******
|
||||
|
||||
A **wallet** manages a private/public key pair which is used to cryptographically sign
|
||||
transactions and prove ownership on the Ethereum network.
|
||||
|
||||
|
||||
-----
|
||||
|
||||
|
||||
Creating Instances
|
||||
==================
|
||||
|
||||
new :sup:`ethers` . Wallet( privateKey [ , provider ] )
|
||||
Creates a new instance from *privateKey* and optionally connect a provider
|
||||
|
||||
:sup:`ethers . Wallet` . createRandom ( [ options ] )
|
||||
Creates a new random wallet; *options* may specify ``extraEntropy`` to stir into
|
||||
the random source (make sure this wallet is stored somewhere safe; if lost there
|
||||
is no way to recover it)
|
||||
|
||||
:sup:`ethers . Wallet` . fromEncryptedWallet ( json, password [ , progressCallback ] )
|
||||
Decrypt an encrypted Secret Storage JSON Wallet (from Geth, or that was
|
||||
created using *prototype.encrypt* )
|
||||
|
||||
:sup:`ethers . Wallet` . fromMnemonic ( mnemonic [ , path ] )
|
||||
Generate a BIP39 + BIP32 wallet from a *mnemonic* deriving path
|
||||
|
||||
**default:** *path*\ ="m/44'/60'/0'/0/0"
|
||||
|
||||
:sup:`ethers . Wallet` . fromBrainWallet ( username , password [ , progressCallback ] )
|
||||
Generate a wallet from a username and password
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
**Private Key** ::
|
||||
|
||||
var privateKey = "0x0123456789012345678901234567890123456789012345678901234567890123";
|
||||
var wallet = new Wallet(privateKey);
|
||||
|
||||
console.log("Address: " + wallet.address);
|
||||
// "Address: 0x14791697260E4c9A71f18484C9f997B308e59325"
|
||||
|
||||
**Random Wallet** ::
|
||||
|
||||
var wallet = Wallet.createRandom();
|
||||
console.log("Address: " + wallet.address);
|
||||
// "Address: ... this will be different every time ..."
|
||||
|
||||
|
||||
**Secret Storage Wallet** (e.g. Geth or Parity) ::
|
||||
|
||||
var data = {
|
||||
id: "fb1280c0-d646-4e40-9550-7026b1be504a",
|
||||
address: "88a5c2d9919e46f883eb62f7b8dd9d0cc45bc290",
|
||||
Crypto: {
|
||||
kdfparams: {
|
||||
dklen: 32,
|
||||
p: 1,
|
||||
salt: "bbfa53547e3e3bfcc9786a2cbef8504a5031d82734ecef02153e29daeed658fd",
|
||||
r: 8,
|
||||
n: 262144
|
||||
},
|
||||
kdf: "scrypt",
|
||||
ciphertext: "10adcc8bcaf49474c6710460e0dc974331f71ee4c7baa7314b4a23d25fd6c406",
|
||||
mac: "1cf53b5ae8d75f8c037b453e7c3c61b010225d916768a6b145adf5cf9cb3a703",
|
||||
cipher: "aes-128-ctr",
|
||||
cipherparams: {
|
||||
iv: "1dcdf13e49cea706994ed38804f6d171"
|
||||
}
|
||||
},
|
||||
"version" : 3
|
||||
};
|
||||
|
||||
var json = JSON.stringify(data);
|
||||
var password = "foo";
|
||||
Wallet.fromEncryptedWallet(json, password).then(function(wallet) {
|
||||
console.log("Address: " + wallet.address);
|
||||
// "Address: 0x88a5C2d9919e46F883EB62F7b8Dd9d0CC45bc290"
|
||||
});
|
||||
|
||||
|
||||
**Mnemonic Phrase** ::
|
||||
|
||||
var mnemonic = "radar blur cabbage chef fix engine embark joy scheme fiction master release";
|
||||
var wallet = Wallet.fromMnemonic(mnemonic);
|
||||
|
||||
console.log("Address: " + wallet.address);
|
||||
// "Address: 0xaC39b311DCEb2A4b2f5d8461c1cdaF756F4F7Ae9"
|
||||
|
||||
|
||||
**Brain Wallet** ::
|
||||
|
||||
var username = "support@ethers.io";
|
||||
var password = "password123";
|
||||
Wallet.fromBrainWallet(username, password).then(function(wallet) {
|
||||
console.log("Address: " + wallet.address);
|
||||
// "Address: 0x7Ee9AE2a2eAF3F0df8D323d555479be562ac4905"
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
Prototype
|
||||
=========
|
||||
|
||||
|
||||
.. _address:
|
||||
|
||||
:sup:`prototype` . address
|
||||
The public address of a wallet
|
||||
|
||||
:sup:`prototype` . privateKey
|
||||
The private key of a wallet; keep this secret
|
||||
|
||||
:sup:`prototype` . provider
|
||||
Optional; a connected :ref:`provider` which allows the wallet to connect to
|
||||
the Ethereum network to query its state and send transactions
|
||||
|
||||
:sup:`prototype` . getAddress ( )
|
||||
A function which returns the address; for Wallet, this simply returns the
|
||||
`address`_ property
|
||||
|
||||
:sup:`prototype` . sign ( transaction )
|
||||
Signs *transaction* and returns the signed transaction as a :ref:`hex string <hexstring>`.
|
||||
See :ref:`Transaction Requests <transactionrequest>`.
|
||||
|
||||
:sup:`prototype` . signMessage ( message )
|
||||
Signs *message* and returns the signature as a :ref:`hex string <hexstring>`.
|
||||
|
||||
:sup:`prototype` . encrypt ( password [ , options ] [ , progressCallback ] )
|
||||
Returns a Promise with the wallet encrypted as a Secret Storage JSON Wallet;
|
||||
*options* may include overrides for the scrypt parameters.
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
**Signing Transactions** ::
|
||||
|
||||
var ethers = require('ethers');
|
||||
var Wallet = ethers.Wallet;
|
||||
var utils = ethers.utils;
|
||||
var providers = ethers.providers;
|
||||
|
||||
var privateKey = "0x0123456789012345678901234567890123456789012345678901234567890123";
|
||||
var wallet = new Wallet(privateKey);
|
||||
|
||||
console.log('Address: ' + wallet.address);
|
||||
// "Address: 0x14791697260E4c9A71f18484C9f997B308e59325".
|
||||
|
||||
var transaction = {
|
||||
nonce: 0,
|
||||
gasLimit: 21000,
|
||||
gasPrice: utils.bigNumberify("20000000000"),
|
||||
|
||||
to: "0x88a5C2d9919e46F883EB62F7b8Dd9d0CC45bc290",
|
||||
|
||||
value: utils.parseEther("1.0"),
|
||||
data: "0x",
|
||||
|
||||
// This ensures the transaction cannot be replayed on different networks
|
||||
chainId: providers.networks.homestead.chainId
|
||||
|
||||
};
|
||||
|
||||
var signedTransaction = wallet.sign(transaction);
|
||||
|
||||
console.log(signedTransaction);
|
||||
// "0xf86c808504a817c8008252089488a5c2d9919e46f883eb62f7b8dd9d0cc45bc2" +
|
||||
// "90880de0b6b3a7640000801ca0d7b10eee694f7fd9acaa0baf51e91da5c3d324" +
|
||||
// "f67ad827fbe4410a32967cbc32a06ffb0b4ac0855f146ff82bef010f6f2729b4" +
|
||||
// "24c57b3be967e2074220fca13e79"
|
||||
|
||||
// This can now be sent to the Ethereum network
|
||||
var provider = providers.getDefaultProvider();
|
||||
provider.sendTransaction(signedTransaction).then(function(hash) {
|
||||
console.log('Hash: ' + hash);
|
||||
// Hash:
|
||||
});
|
||||
|
||||
**Encrypting** ::
|
||||
|
||||
var password = "password123";
|
||||
|
||||
function callback(percent) {
|
||||
console.log("Encrypting: " + parseInt(percent * 100) + "% complete");
|
||||
}
|
||||
|
||||
var encryptPromise = wallet.encrypt(password, callback);
|
||||
|
||||
encryptPromise.then(function(json) {
|
||||
console.log(json);
|
||||
});
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Blockchain Operations
|
||||
=====================
|
||||
|
||||
These operations require the wallet have a provider attached to it.
|
||||
|
||||
:sup:`prototype` . getBalance ( [ blockTag ] )
|
||||
Returns a Promise with the balance of the wallet (as a :ref:`BigNumber <bignumber>`,
|
||||
in **wei**) at the :ref:`blockTag <blocktag>`.
|
||||
|
||||
**default:** *blockTag*\ ="latest"
|
||||
|
||||
:sup:`prototype` . getTransactionCount ( [ blockTag ] )
|
||||
Returns a Promise with the number of transactions this account has ever sent
|
||||
(also called the *nonce*) at the :ref:`blockTag <blocktag>`.
|
||||
|
||||
**default:** *blockTag*\ ="latest"
|
||||
|
||||
:sup:`prototype` . estimateGas ( transaction )
|
||||
Returns a Promise with the estimated cost for *transaction* (in **gas**, as a
|
||||
:ref:`BigNumber <bignumber>`)
|
||||
|
||||
:sup:`prototype` . sendTransaction ( transaction )
|
||||
Sends the *transaction* to the network and returns a Promise with the transaction
|
||||
details. It is highly recommended to omit *transaction.chainId*, it will be
|
||||
filled in by *provider*.
|
||||
|
||||
:sup:`prototype` . send ( addressOrName, amountWei [ , options ] )
|
||||
Sends *amountWei* to *addressOrName* on the network and returns a Promise with the
|
||||
transaction details.
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
**Query the Network** ::
|
||||
|
||||
var privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
|
||||
var wallet = new ethers.Wallet(privateKey);
|
||||
wallet.provider = ethers.providers.getDefaultProvider();
|
||||
|
||||
var balancePromise = wallet.getBalance();
|
||||
|
||||
balancePromise.then(function(balance) {
|
||||
console.log(balance);
|
||||
});
|
||||
|
||||
var transactionCountPromise = wallet.getTransactionCount();
|
||||
|
||||
transactionCountPromise.then(function(transactionCount) {
|
||||
console.log(transactionCount);
|
||||
});
|
||||
|
||||
|
||||
|
||||
**Transfer Ether** ::
|
||||
|
||||
var privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
|
||||
var wallet = new ethers.Wallet(privateKey);
|
||||
wallet.provider = ethers.providers.getDefaultProvider();
|
||||
|
||||
// We must pass in the amount as wei (1 ether = 1e18 wei), so we use
|
||||
// this convenience function to convert ether to wei.
|
||||
var amount = ethers.utils.parseEther('1.0');
|
||||
|
||||
var address = '0x88a5c2d9919e46f883eb62f7b8dd9d0cc45bc290';
|
||||
var sendPromise = wallet.send(address, amount);
|
||||
|
||||
sendPromise.then(function(transactionHash) {
|
||||
console.log(transactionHash);
|
||||
});
|
||||
|
||||
|
||||
// These will query the network for appropriate values
|
||||
var options = {
|
||||
//gasLimit: 21000
|
||||
//gasPrice: utils.bigNumberify("20000000000")
|
||||
};
|
||||
|
||||
var promiseSend = wallet.send(address, amount, options);
|
||||
|
||||
promiseSend.then(function(transaction) {
|
||||
console.log(transaction);
|
||||
});
|
||||
|
||||
|
||||
**Sending (Complex) Transactions** ::
|
||||
|
||||
var privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
|
||||
var wallet = new ethers.Wallet(privateKey);
|
||||
wallet.provider = ethers.providers.getDefaultProvider('ropsten');
|
||||
|
||||
var transaction = {
|
||||
// Recommendation: omit nonce; the provider will query the network
|
||||
// nonce: 0,
|
||||
|
||||
// Gas Limit; 21000 will send ether to another use, but to execute contracts
|
||||
// larger limits are required. The provider.estimateGas can be used for this.
|
||||
gasLimit: 1000000,
|
||||
|
||||
// Recommendations: omit gasPrice; the provider will query the network
|
||||
//gasPrice: utils.bigNumberify("20000000000"),
|
||||
|
||||
// Required; unless deploying a contract (in which case omit)
|
||||
to: "0x88a5C2d9919e46F883EB62F7b8Dd9d0CC45bc290",
|
||||
|
||||
// Optional
|
||||
data: "0x",
|
||||
|
||||
// Optional
|
||||
value: ethers.utils.parseEther("1.0"),
|
||||
|
||||
// Recommendation: omit chainId; the provider will populate this
|
||||
// chaindId: providers.networks.homestead.chainId
|
||||
};
|
||||
|
||||
// Estimate the gas cost for the transaction
|
||||
//var estimateGasPromise = wallet.estimateGas(transaction);
|
||||
|
||||
//estimateGasPromise.then(function(gasEstimate) {
|
||||
// console.log(gasEstimate);
|
||||
//});
|
||||
|
||||
// Send the transaction
|
||||
var sendTransactionPromise = wallet.sendTransaction(transaction);
|
||||
|
||||
sendTransactionPromise.then(function(transactionHash) {
|
||||
console.log(transactionHash);
|
||||
});
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Parsing Transactions
|
||||
====================
|
||||
|
||||
:sup:`Wallet` . parseTransaction ( hexStringOrArrayish )
|
||||
Parses a raw *hexStringOrArrayish* into a Transaction.
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
// Mainnet:
|
||||
var ethers = require('ethers');
|
||||
var Wallet = ethers.Wallet;
|
||||
var utils = ethers.utils;
|
||||
var privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
|
||||
var wallet = new ethers.Wallet(privateKey);
|
||||
|
||||
var raw = "0xf87083154262850500cf6e0083015f9094c149be1bcdfa69a94384b46a1f913" +
|
||||
"50e5f81c1ab880de6c75de74c236c8025a05b13ef45ce3faf69d1f40f9d15b007" +
|
||||
"0cc9e2c92f"
|
||||
|
||||
var transaction = {
|
||||
nonce: 1393250,
|
||||
gasLimit: 21000,
|
||||
gasPrice: utils.bigNumberify("20000000000"),
|
||||
|
||||
to: "0xc149Be1bcDFa69a94384b46A1F91350E5f81c1AB",
|
||||
|
||||
value: utils.parseEther("1.0"),
|
||||
data: "0x",
|
||||
|
||||
// This ensures the transaction cannot be replayed on different networks
|
||||
chainId: ethers.providers.networks.homestead.chainId
|
||||
};
|
||||
|
||||
var signedTransaction = wallet.sign(transaction);
|
||||
var transaction = Wallet.parseTransaction(signedTransaction);
|
||||
|
||||
console.log(transaction);
|
||||
// { nonce: 1393250,
|
||||
// gasPrice: BigNumber { _bn: <BN: 4a817c800> },
|
||||
// gasLimit: BigNumber { _bn: <BN: 5208> },
|
||||
// to: '0xc149Be1bcDFa69a94384b46A1F91350E5f81c1AB',
|
||||
// value: BigNumber { _bn: <BN: de0b6b3a7640000> },
|
||||
// data: '0x',
|
||||
// v: 38,
|
||||
// r: '0x3cf1f5af8bd11963193451096d86635aed589572c184ac8696dd99c9c044ded3',
|
||||
// s: '0x08c52dbf1383492c72598511bb135179ec93b062032d2a0d002214644ba39a2c',
|
||||
// chainId: 1,
|
||||
// from: '0x14791697260E4c9A71f18484C9f997B308e59325' }
|
||||
|
||||
-----
|
||||
|
||||
Verifying Messages
|
||||
==================
|
||||
|
||||
:sup:`ethers . Wallet` . verifyMessage ( message , signature )
|
||||
Returns the address that signed *message* with *signature*.
|
||||
|
||||
*Examples*
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
var signature = "0xddd0a7290af9526056b4e35a077b9a11b513aa0028ec6c9880948544508f3c63" +
|
||||
"265e99e47ad31bb2cab9646c504576b3abc6939a1710afc08cbf3034d73214b8" +
|
||||
"1c";
|
||||
var address = Wallet.verifyMessage('hello world', signature);
|
||||
console.log(address);
|
||||
// '0x14791697260E4c9A71f18484C9f997B308e59325'
|
||||
|
||||
-----
|
||||
|
||||
.. EOF
|
@ -1,16 +0,0 @@
|
||||
.. _api:
|
||||
|
||||
Application Programming Interface (API)
|
||||
***************************************
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api-wallet
|
||||
api-providers
|
||||
api-contract
|
||||
api-utils
|
||||
|
||||
-----
|
||||
|
||||
.. EOF
|
@ -1,259 +0,0 @@
|
||||
Cookbook
|
||||
********
|
||||
|
||||
This is a small (but growing) collection of simple recipes to perform common tasks
|
||||
with the Ethereum blockchain and Ethereum accounts.
|
||||
|
||||
Some of these recipes are stubs that will be filled in shortly.
|
||||
|
||||
If there is a simple recipe you would like added, please send suggestions to support@ethers.io.
|
||||
|
||||
-----
|
||||
|
||||
Dump All JSON Wallet Balances (in current directory)
|
||||
====================================================
|
||||
|
||||
::
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
var ethers = require('ethers');
|
||||
var provider = ethers.providers.getDefaultProvider();
|
||||
|
||||
// Geth
|
||||
var dirname = path.join(process.env.HOME, '.ethereum', 'keystore');
|
||||
|
||||
// Parity (use the name of your chain for chainDirectory, such as "homestead")
|
||||
//var dirname = path.join(process.env.HOME, '.parity', 'keys', chainDirectory, 'keys');
|
||||
|
||||
var filenames = fs.readdirSync(dirname);
|
||||
|
||||
filenames.forEach(function(filename) {
|
||||
fs.readFile(path.join(dirname,filename), function(error, data) {
|
||||
if (error) {
|
||||
console.log('Error reading file: ' + error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
var address = JSON.parse(data.toString()).address;
|
||||
provider.getBalance(address).then(function(balance) {
|
||||
console.log(address + ':' + ethers.utils.formatEther(balance));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Empty One Account into Another
|
||||
==============================
|
||||
|
||||
::
|
||||
|
||||
var ethers = require('ethers');
|
||||
|
||||
var provider = ethers.providers.getDefaultProvider();
|
||||
|
||||
var newAddress = '';
|
||||
var privateKey = '';
|
||||
|
||||
var wallet = new ethers.Wallet(privateKey, provider);
|
||||
Promise.all([
|
||||
wallet.getBalance(),
|
||||
provider.getGasPrice(),
|
||||
provider.getCode(newAddress)
|
||||
]).then(function(results) {
|
||||
var balance = results[0];
|
||||
var gasPrice = results[1];
|
||||
var code = results[2];
|
||||
|
||||
if (code !== '0x') {
|
||||
throw new Error('this tool should not send to a contract');
|
||||
}
|
||||
|
||||
// The exact cost (in gas) to send to an Externally Owned Account (EOA)
|
||||
var gasLimit = 21000;
|
||||
|
||||
// The balance less exactly the txfee in wei
|
||||
var value = balance.sub(gasPrice.mul(gasLimit))
|
||||
|
||||
wallet.send(newAddress, value, {gasLimit: gasLimit}).then(function(transaction) {
|
||||
console.log(transaction);
|
||||
});
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
Transactions Confirm UI (with a Custom Signer)
|
||||
==============================================
|
||||
|
||||
::
|
||||
|
||||
function CustomSigner(privateKey) {
|
||||
|
||||
this.provider = ethers.getDefaultProvider();
|
||||
|
||||
var wallet = new ethers.Wallet(privateKey);
|
||||
|
||||
this.address = wallet.address;
|
||||
|
||||
this.sign = function(transaction) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var allow = confirm('Sign Transaction? To: ' + transaction.to +
|
||||
", Amount: " + ethers.formatEther(transaction.value));
|
||||
|
||||
var etherString = ethers.formatEther(transaction.value);
|
||||
|
||||
var modal = document.createElement('pre');
|
||||
document.body.appendChild(modal);
|
||||
modal.className = "modal";
|
||||
modal.textContent += 'Sign Transaction?\n';
|
||||
modal.textContent += 'To: ' + transaction.address + '\n';
|
||||
modal.textContent += 'Amount: ' + etherString + '\n';
|
||||
|
||||
var confirmButton = document.createElement('div');
|
||||
modal.appendChild(confirmButton);
|
||||
confirmButton.textContent = ""confirm";
|
||||
confirmButton.onclick = function() {
|
||||
resolve(wallet.sign(transaction));
|
||||
}
|
||||
|
||||
var rejectButton = document.createElement('div');
|
||||
modal.appendChild(rejectButton);
|
||||
rejectButton.textContent = ""confirm";
|
||||
rejectButton.onclick = function() {
|
||||
modal.remove();
|
||||
reject(new Error('cancelled transaction'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-----
|
||||
|
||||
Break Apart r, s and v from a Message Signature
|
||||
================================================
|
||||
|
||||
::
|
||||
|
||||
var ethers = require('ethers');
|
||||
|
||||
var privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
|
||||
var message = "Hello World";
|
||||
var wallet = new ethers.Wallet(privateKey);
|
||||
var signature = wallet.signMessage(message);
|
||||
|
||||
// Split apart the signature into an r, s and v that can be used by
|
||||
// ecrecover in Solidity. The v parameter will be normalized to the
|
||||
// canonical value of 27 or 28.
|
||||
var sig = ethers.utils.splitSignature(signature);
|
||||
|
||||
console.log(sig);
|
||||
// {
|
||||
// r: '0xe0ed34fbbe927a58267ce2e8067a611c69869e20e731bc99187a8bc97058664c',
|
||||
// s: '0x16de07f7660f06ce0985d1d8e063726783033fda59b307897f26a21392d62b3a',
|
||||
// v: 28
|
||||
// }
|
||||
|
||||
-----
|
||||
|
||||
Coalesce Jaxx Wallets
|
||||
=====================
|
||||
|
||||
The Jaxx Wallet (for iOS, Android, desktop, et cetera) uses HD wallets on Ethereum the
|
||||
same way as Bitcoin, which results in each transaction being received by a separate
|
||||
address. As a result, funds get spread across many accounts, making several operations
|
||||
in Ethereum impossible.
|
||||
|
||||
This short recipe will coalesce all these accounts into a single one, by sending the funds
|
||||
from each account into a single one.
|
||||
|
||||
This also results in paying multiple transaction fees (1 fee per account to merge).
|
||||
|
||||
@TODO: This is incomplete!!
|
||||
|
||||
*Source Code*
|
||||
-------------
|
||||
|
||||
::
|
||||
|
||||
var ethers = require('ethers');
|
||||
|
||||
var provider = ethers.providers.getDefaultProvider();
|
||||
|
||||
var mnemonic = "radar blur cabbage chef fix engine embark joy scheme fiction master release";
|
||||
var hdnode = ethers.HDNode.fromMnemonic(mnemonic);
|
||||
hdnode = hdnode.derivePath("m/44'/60'/0'/0");
|
||||
|
||||
@TODO:
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Access Funds in a Mnemonic Phrase Wallet
|
||||
========================================
|
||||
|
||||
@TODO: This is incomplete
|
||||
|
||||
*Source Code*
|
||||
-------------
|
||||
|
||||
::
|
||||
|
||||
var ethers = require('ethers');
|
||||
|
||||
var walletPath = {
|
||||
"standard": "m/44'/60'/0'/0/0",
|
||||
|
||||
// @TODO: Include some non-standard wallet paths
|
||||
};
|
||||
|
||||
var mnemonic = "radar blur cabbage chef fix engine embark joy scheme fiction master release";
|
||||
var hdnode = ethers.HDNode.fromMnemonic(mnemonic);
|
||||
var node = hdnode.derivePath(walletPath.standard);
|
||||
|
||||
var wallet = new ethers.Wallet(node.privateKey);
|
||||
console.log(wallet.address);
|
||||
// 0xaC39b311DCEb2A4b2f5d8461c1cdaF756F4F7Ae9
|
||||
|
||||
@TODO:
|
||||
|
||||
-----
|
||||
|
||||
Custom Provider
|
||||
===============
|
||||
|
||||
This is a much more advanced topic, and most people should not need to work this
|
||||
low level. But it is provided for those rare instances where you need some custom
|
||||
connection logic.
|
||||
|
||||
A provider must only implement the method **perform(method, params)**. All data passed
|
||||
into a provider is sanitized by the Provider subclass, and all results are normalized
|
||||
before returning them to the user.
|
||||
|
||||
For this example, we will build a DebugProvider, which will simple proxy all commands
|
||||
through to INFURA, but dump all data going back and forth.
|
||||
|
||||
::
|
||||
|
||||
var inherits = require('inherits');
|
||||
var ethers = require('ethers');
|
||||
|
||||
function DebugProvider(testnet) {
|
||||
Provider.call(this, testnet);
|
||||
this.subprovider = new ethers.providers.InfuraProvider(testnet);
|
||||
}
|
||||
inherits(DebugProvider, ethers.providers.Provider);
|
||||
|
||||
// This should return a Promise (and may throw erros)
|
||||
// method is the method name (e.g. getBalance) and params is an
|
||||
// object with normalized values passed in, depending on the method
|
||||
DebugProvier.prototype.perform = function(method, params) {
|
||||
this.subprovider.perform(method, params).then(function(result) {
|
||||
console.log('DEBUG', method, params, '=>', result);
|
||||
});
|
||||
}
|
||||
|
||||
-----
|
||||
|
||||
.. EOF
|
@ -1,39 +0,0 @@
|
||||
Getting Started
|
||||
***************
|
||||
|
||||
The ethers.js library is a compact and complete JavaScript library for Ethereum.
|
||||
|
||||
-----
|
||||
|
||||
Installing in Node.js
|
||||
=====================
|
||||
|
||||
From your project directory::
|
||||
|
||||
/Users/ricmoo/my-project> npm install --save ethers
|
||||
|
||||
And from the relevant application files::
|
||||
|
||||
var ethers = require('ethers');
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Including in Web Applications
|
||||
=============================
|
||||
|
||||
For security purposes, it is usually best to place a copy of `this script`_ on
|
||||
the application's server, but for a quick prototype using the Ethers CDN (content
|
||||
distribution network) should suffice::
|
||||
|
||||
<!-- This exposes the library as a global variable: ethers -->
|
||||
<script src="https://cdn.ethers.io/scripts/ethers-v3.min.js"
|
||||
charset="utf-8"
|
||||
type="text/javascript">
|
||||
</script>
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _npm is installed: https://nodejs.org/en/
|
||||
.. _this script: https://cdn.ethers.io/scripts/ethers-v3.0.min.js
|
@ -1,27 +0,0 @@
|
||||
.. ethers.js documentation master file, created by
|
||||
sphinx-quickstart on Tue Nov 29 10:25:33 2016.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
|
||||
What is ethers.js
|
||||
*****************
|
||||
|
||||
This library (which was made for and used by ethers.io) is designed to make it
|
||||
easier to write client-side JavaScript based wallets, keeping the private key on
|
||||
the owner's machine at all times.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
:caption: Developer Documentation
|
||||
|
||||
getting-started
|
||||
api
|
||||
api-advanced
|
||||
cookbook
|
||||
notes
|
||||
testing
|
||||
|
||||
-----
|
||||
|
||||
.. EOF
|
@ -1,195 +0,0 @@
|
||||
Notes
|
||||
*****
|
||||
|
||||
A few quick notes about some of the less obvious aspects of interacting with
|
||||
Ethereum in JavaScript.
|
||||
|
||||
-----
|
||||
|
||||
.. _ieee754:
|
||||
|
||||
Why can't I just use numbers?
|
||||
=============================
|
||||
|
||||
The first problem many encounter when dealing with Ethereum is the concept of numbers. Most
|
||||
common currencies are broken down with very little granulairty. For example, there are only
|
||||
100 cents in a single dollar. However, there are 10\ :sup:`18` **wei** in a single
|
||||
**ether**.
|
||||
|
||||
JavaScript uses `IEEE 754 double-precision binary floating point`_ numbers to represent
|
||||
numeric values. As a result, there are *holes* in the integer set after
|
||||
9,007,199,254,740,991; which is problematic for *Ethereum* because that is only
|
||||
around 0.009 ether (in wei).
|
||||
|
||||
To demonstrate how this may be an issue in your code, consider::
|
||||
|
||||
> (Number.MAX_SAFE_INTEGER + 4 - 5) == (Number.MAX_SAFE_INTEGER - 1)
|
||||
false
|
||||
|
||||
|
||||
To remedy this, all numbers (which can be large) are stored and manipulated
|
||||
as :ref:`Big Numbers <bignumber>`.
|
||||
|
||||
The functions :ref:`parseEther( etherString ) <parseEther>` and :ref:`formatEther( wei ) <formatEther>` can be used to convert between
|
||||
string representations, which are displayed to or entered by the user and Big Number representations
|
||||
which can have mathematical operations handled safely.
|
||||
|
||||
-----
|
||||
|
||||
.. _promise:
|
||||
|
||||
Promises
|
||||
========
|
||||
|
||||
A `Promise in JavaScript`_ is an object which simplifies many aspects of dealing with
|
||||
asynchronous functions.
|
||||
|
||||
It allows a pending result to be treated in many ways as if it has already been resolved.
|
||||
|
||||
The most useful operations you will need are:
|
||||
|
||||
:sup:`Promise` . all ( promises )
|
||||
Returns a new promise that resolves once all the *promises* have resolved.
|
||||
|
||||
:sup:`prototype` . then ( onResolve, onReject )
|
||||
Returns another Promise, which once the Promise was resolved, the *onResolve*
|
||||
function will be executed and if an error occurs, *onReject* will be called.
|
||||
|
||||
If *onResolve* returns a Promise, it will be inserted into the chain of the returned
|
||||
promise. If *onResolve* throws an Error, the returned Promise will reject.
|
||||
|
||||
**Examples**
|
||||
------------
|
||||
|
||||
**Cleaning out an account**
|
||||
|
||||
::
|
||||
|
||||
var ethers = require('ethers');
|
||||
var targetAddress = "0x02F024e0882B310c6734703AB9066EdD3a10C6e0";
|
||||
|
||||
var privateKey = "0x0123456789012345678901234567890123456789012345678901234567890123";
|
||||
var wallet = new ethers.Wallet(privateKey);
|
||||
|
||||
// Promises we are interested in
|
||||
var provider = ethers.providers.getDefaultProvider('ropsten');
|
||||
var balancePromise = provider.getBalance(wallet.address);
|
||||
var gasPricePromise = provider.getGasPrice();
|
||||
var transactionCountPromise = provider.getTransactionCount(wallet.address);
|
||||
|
||||
var allPromises = Promise.all([
|
||||
gasPricePromise,
|
||||
balancePromise,
|
||||
transactionCountPromise
|
||||
]);
|
||||
|
||||
var sendPromise = allPromises.then(function(results) {
|
||||
// This function is ONLY called once ALL promises are fulfilled
|
||||
|
||||
var gasPrice = results[0];
|
||||
var balance = results[1];
|
||||
var transactionCount = results[2];
|
||||
|
||||
// Sending a transaction to an externally owned account (EOA) is 21000 gas)
|
||||
var txFeeInWei = gasPrice.mul(21000);
|
||||
|
||||
// This will send the maximum amount (our balance minus the fee)
|
||||
var value = balance.sub(txFeeInWei);
|
||||
|
||||
var transaction = {
|
||||
to: targetAddress,
|
||||
gasPrice: gasPrice,
|
||||
gasLimit: 21000,
|
||||
nonce: transactionCount,
|
||||
|
||||
// The amount to send
|
||||
value: value,
|
||||
|
||||
// Prevent replay attacks across networks
|
||||
chainId: provider.chainId,
|
||||
};
|
||||
|
||||
var signedTransaction = wallet.sign(transaction);
|
||||
|
||||
// By returning a Promise, the sendPromise will resolve once the
|
||||
// transaction is sent
|
||||
return provider.sendTransaction(signedTransaction);
|
||||
});
|
||||
|
||||
var minedPromise = sendPromise.then(function(transaction) {
|
||||
// This will be called once the transaction is sent
|
||||
|
||||
// This promise will be resolve once the transaction has been mined.
|
||||
return provider.waitForTransaction(transaction);
|
||||
});
|
||||
|
||||
minedPromise.then(function(transaction) {
|
||||
console.log("The transaction was mined: Block " + transaction.blockNumber);
|
||||
});
|
||||
|
||||
|
||||
// Promises can be re-used for their value; it will not make the external
|
||||
// call again, and will provide the exact same result every time.
|
||||
balancePromise.then(function(balance) {
|
||||
// This *may* return before teh above allPromises, since it only
|
||||
// required one external call. Keep in mind asynchronous calls can
|
||||
// be called out of order.
|
||||
console.log(balance);
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
.. _checksum-address:
|
||||
|
||||
Checksum Address
|
||||
================
|
||||
|
||||
A `checksum address`_ uses mixed case hexidecimal strings to encode the checksum
|
||||
information in the capitalization of the alphabetic characters, while remaining
|
||||
backwards compatible with non-checksum addresses.
|
||||
|
||||
Example::
|
||||
|
||||
// Valid; checksum (mixed case)
|
||||
0xCd2a3d9f938e13Cd947eC05ABC7fe734df8DD826
|
||||
|
||||
// Valid; NO checksum (no mixed case)
|
||||
0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826
|
||||
0xCD2A3D9F938E13CD947EC05ABC7FE734DF8DD826
|
||||
|
||||
// INVALID; (mixed case, but case differs from first example)
|
||||
0xDc2a3d9f938e13cd947ec05abc7fe734df8dd826
|
||||
^^
|
||||
|
||||
To convert between ICAP and checksum addresses, see :ref:`getAddress() <api-getAddress>`.
|
||||
|
||||
.. _checksum address: https://github.com/ethereum/EIPs/issues/55
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _icap-address:
|
||||
|
||||
ICAP Address
|
||||
============
|
||||
|
||||
The original method of adding a checksum to an Ethereum address was by using the
|
||||
a format compatible with `IBAN`_ addresses, using the country code **XE**. However,
|
||||
only addresses which have 0 as the first byte (i.e. the address begins with 0x00)
|
||||
are truely compatible with IBAN, so ICAP extends the definition to allow for 31
|
||||
alphanumeric characters (instead of the standard 30).
|
||||
|
||||
An ICAP address has the following format::
|
||||
|
||||
XE [2 digit checksum] [up to 31 alphanumeric characters]
|
||||
|
||||
To convert between ICAP and checksum addresses, see :ref:`getAddress() <api-getAddress>`.
|
||||
|
||||
-----
|
||||
|
||||
.. _IBAN: https://en.wikipedia.org/wiki/International_Bank_Account_Number
|
||||
.. _IEEE 754 double-precision binary floating point: https://en.wikipedia.org/wiki/Double-precision_floating-point_format
|
||||
.. _BN.js: https://github.com/indutny/bn.js/
|
||||
.. _Promise in JavaScript: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
|
||||
|
||||
.. EOF
|
@ -1,19 +0,0 @@
|
||||
Testing
|
||||
*******
|
||||
|
||||
Ethers uses a large suite of test cases to help ensure the library is as
|
||||
complete, backwards compatible and correct as possible and pass
|
||||
regression as new features are added.
|
||||
|
||||
Many of the test cases are created procedurally.
|
||||
|
||||
**@TODO:**
|
||||
Explain more here on how to run and add testcases.
|
||||
|
||||
**@TODO:**
|
||||
Post links to the testcases on IPFS (the generated test cases can takes hours to
|
||||
generate and are too large to check into GitHub)
|
||||
|
||||
-----
|
||||
|
||||
.. EOF
|
@ -1,565 +0,0 @@
|
||||
.. |nbsp| unicode:: U+00A0 .. non-breaking space
|
||||
|
||||
Low-Level API
|
||||
**************
|
||||
|
||||
These are advanced, low-level API features that should, for most people not be
|
||||
necessary to worry about.
|
||||
|
||||
They are lightly documented here, and in the future will have more documentation,
|
||||
but the emphasis at this point is documenting the more :ref:`common methods <api>`.
|
||||
|
||||
-----
|
||||
|
||||
ABI Coder
|
||||
=========
|
||||
|
||||
Creating Instances
|
||||
------------------
|
||||
|
||||
new :sup:`ethers . utils` **. AbiCoder** ( [ coerceFunc ] )
|
||||
Create a new ABI Coder object, which calls *coerceFunc* for each parsed value
|
||||
during decoding. The *coerceFunc* should have the signature: ``function(type, value)``.
|
||||
|
||||
Static Properties
|
||||
-----------------
|
||||
|
||||
:sup:`ethers . utils` **. defaultAbiCoder**
|
||||
A default instance of the coder which can be used, which has a *coerceFunc*
|
||||
which will call ``toNumber()`` on BigNumbers whose **type** is less than
|
||||
53 bits and is safe for JavaScript Number instances.
|
||||
|
||||
Prototype
|
||||
---------
|
||||
|
||||
:sup:`prototype` . encode ( types , values ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Returns a :ref:`hex string <hexstring>` of the *values* encoded as the *types*.
|
||||
Throws if a value is invalid for the type.
|
||||
|
||||
:sup:`prototype` . decode ( types , data ) |nbsp| :sup:`=>` |nbsp| :sup:`Result`
|
||||
Returns an Object by parsing *data* assuming *types*, with each parameter
|
||||
accessible as a positional parameters. Throws if *data* is invalid
|
||||
for the *types*.
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _api-hdnode:
|
||||
|
||||
HDNode
|
||||
======
|
||||
|
||||
A *Hierarchical Deterministic Wallet* represents a large tree of private keys
|
||||
which can reliably be reproduced from an initial seed. Each node in the tree
|
||||
is represented by an HDNode which can be descended into.
|
||||
|
||||
A *mnemonic phrase* represents a simple way to generate the initial seed.
|
||||
|
||||
See the `BIP 32 Specification`_ to learn more about HD Wallets and hardened vs
|
||||
non-hardened nodes.
|
||||
|
||||
See the `BIP 39 Specification`_ to learn more about Mnemonic Phrases.
|
||||
|
||||
Creating Instances
|
||||
------------------
|
||||
|
||||
:sup:`ethers . utils . HDNode` **. fromMnemonic** ( mnemonic ) |nbsp| :sup:`=>` |nbsp| :sup:`HDNode`
|
||||
Create an HDNode from a *mnemonic* phrase.
|
||||
|
||||
:sup:`ethers . utils . HDNode` **. fromSeed** ( seed ) |nbsp| :sup:`=>` |nbsp| :sup:`HDNode`
|
||||
Create an HDNode from a seed.
|
||||
|
||||
:sup:`ethers . utils . HDNode` **. fromExtendedKey** ( extendedKey ) |nbsp| :sup:`=>` |nbsp| :sup:`HDNode`
|
||||
Create an HDNode from an extended private key (xpriv) or extended public key (xpub).
|
||||
|
||||
|
||||
Prototype
|
||||
---------
|
||||
|
||||
:sup:`prototype` **. privateKey**
|
||||
The :ref:`hex string <hexstring>` private key for this node.
|
||||
|
||||
:sup:`prototype` **. publicKey**
|
||||
The (compressed) public key for this node.
|
||||
|
||||
:sup:`prototype` **. chainCode**
|
||||
The chain code for this node.
|
||||
|
||||
:sup:`prototype` **. index**
|
||||
The index (from the parent) of this node (0 for the master node).
|
||||
|
||||
:sup:`prototype` **. depth**
|
||||
The depth within the hierarchy of this node.
|
||||
|
||||
:sup:`prototype` **. fingerprint**
|
||||
The fingerprint of this node. This can be used to identify a node, but wallets
|
||||
should handle collisions.
|
||||
|
||||
:sup:`prototype` **. parentFingerprint**
|
||||
The fingerprint of this node's parent (or 0x00000000 for the master node).
|
||||
|
||||
:sup:`prototype` **. extendedKey**
|
||||
The extended private key (xpriv) of the node, or the extended public key (xpub)
|
||||
if the node has been neutered.
|
||||
|
||||
|
||||
Deriving Child and Neutered Nodes
|
||||
---------------------------------
|
||||
|
||||
:sup:`prototype` **. derivePath** ( path ) |nbsp| :sup:`=>` |nbsp| :sup:`HDNode`
|
||||
Derive the path from this node. Path is slash (**/**) delimited path components.
|
||||
The first component may be "m" for master (which enforces the starting node is
|
||||
in fact a master node) and each subsequent path component should be a positive
|
||||
integer (up to 31 bits), which can optionally include an apostrophe (**'**) to
|
||||
indicate hardened derivation for that path components. See below for some examples.
|
||||
|
||||
:sup:`prototype` **. neuter** ( ) |nbsp| :sup:`=>` |nbsp| :sup:`HDNode`
|
||||
Returns a new instance of the node without a private key. This can be used to
|
||||
derive an extended public key. See the BIP32 standard for more details.
|
||||
|
||||
|
||||
Static Methods
|
||||
--------------
|
||||
|
||||
:sup:`ethers . utils . HDNode` **. mnemonicToEntropy** ( mnemonic ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Convert a *mnemonic* to its binary entropy. (throws an error if the checksum
|
||||
is invalid)
|
||||
|
||||
:sup:`ethers . utils . HDNode` **. entropyToMnemonic** ( entropy ) |nbsp| :sup:`=>` |nbsp| :sup:`string`
|
||||
Convert the binary *entropy* to the mnemonic phrase.
|
||||
|
||||
:sup:`ethers . utils . HDNode` **. mnemonicToSeed** ( mnemonic ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Compute the BIP39 seed from *mnemonic*.
|
||||
|
||||
:sup:`ethers . utils . HDNode` **. isValidMnemonic** ( string ) |nbsp| :sup:`=>` |nbsp| :sup:`boolean`
|
||||
Returns true if and only if the string is a valid mnemonic (including
|
||||
the checksum)
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *HDNode derivation*
|
||||
|
||||
let HDNode = require('ethers').utils.HDNode;
|
||||
|
||||
let mnemonic = "radar blur cabbage chef fix engine embark joy scheme fiction master release";
|
||||
|
||||
let masterNode = HDNode.fromMnemonic(mnemonic);
|
||||
|
||||
let standardEthereum = masterNode.derivePath("m/44'/60'/0'/0/0");
|
||||
|
||||
// Get the extended private key
|
||||
let xpriv = node.extendedKey;
|
||||
|
||||
// Get the extended public key
|
||||
let xpub = node.neuter().extnededKey;
|
||||
|
||||
-----
|
||||
|
||||
.. _api-interface:
|
||||
|
||||
Interface
|
||||
=========
|
||||
|
||||
The Interface Object is a meta-class that accepts a Solidity (or compatible)
|
||||
Application Binary Interface (ABI) and populates functions to deal with encoding
|
||||
and decoding the parameters to pass in and results returned.
|
||||
|
||||
Creating an Instance
|
||||
--------------------
|
||||
|
||||
new :sup:`ethers . utils` . Interface ( abi )
|
||||
Returns a new instance and populates the properties with the ABI constructor,
|
||||
methods and events. The *abi* may be either a JSON string or the parsed JSON
|
||||
Object.
|
||||
|
||||
|
||||
Prototype
|
||||
---------
|
||||
|
||||
:sup:`prototype` . abi
|
||||
A **copy** of the ABI is returned, modifying this object will not alter the ABI.
|
||||
|
||||
:sup:`prototype` . deployFunction
|
||||
A DeployDesciption for the constructor defined in the ABI, or the default constructor
|
||||
if omitted.
|
||||
|
||||
:sup:`prototype` . events
|
||||
An object of all the events available in the ABI, by name and signature, which map
|
||||
to a EventDescription.
|
||||
|
||||
:sup:`prototype` . functions
|
||||
An object of all the functions available in the ABI, by name and signature, which map
|
||||
to a FunctionDescription.
|
||||
|
||||
|
||||
Parsing Objects
|
||||
---------------
|
||||
|
||||
:sup:`prototype` . parseTransaction ( transaction ) |nbsp| :sup:`=>` |nbsp| :sup:`TransactionDescription`
|
||||
Parse *transaction* and return a description of the call it represents.
|
||||
|
||||
:sup:`prototype` . parseLog ( log ) |nbsp| :sup:`=>` |nbsp| :sup:`LogDescription`
|
||||
Parse *log* and return a description of the event logs it represents.
|
||||
|
||||
|
||||
Object Test Functions
|
||||
---------------------
|
||||
|
||||
:sup:`prototype` . isInterface ( value ) |nbsp| :sup:`=>` |nbsp| :sup:`boolean`
|
||||
Returns true if *value* is an Interface.
|
||||
|
||||
:sup:`prototype` . isIndexed ( value ) |nbsp| :sup:`=>` |nbsp| :sup:`boolean`
|
||||
Returns true if *value* is a dynamic Indexed value, which means the actual
|
||||
value of *value* is the hash of the actual value.
|
||||
|
||||
|
||||
Descriptions
|
||||
------------
|
||||
|
||||
**Deploy Description**
|
||||
|
||||
============================== ======================================
|
||||
name description
|
||||
============================== ======================================
|
||||
inputs The description of the constructor input parameters
|
||||
payable Whether the constructor can accept *Ether*
|
||||
encode(params) A function which encodes *params*
|
||||
============================== ======================================
|
||||
|
||||
**Event Description**
|
||||
|
||||
============================== ======================================
|
||||
name description
|
||||
============================== ======================================
|
||||
name The event name (e.g. "Transfer")
|
||||
signature The event signature (e.g. "Transfer(address indexed,address indexed,uint256)")
|
||||
inputs The event input parameters
|
||||
anonymous Whether the event is an anonymous event
|
||||
topic The topic for this event signature
|
||||
encodeTopics(params) A function which computes filter topics for given *params*
|
||||
decode(data, topics) A function to parse the log result *data* and *topics*
|
||||
============================== ======================================
|
||||
|
||||
**Function Description**
|
||||
|
||||
============================== ======================================
|
||||
name description
|
||||
============================== ======================================
|
||||
name The method name (e.g. "transfer")
|
||||
type The method type (i.e. "call" or "transaction")
|
||||
signature The method signature (e.g. "transfer(address to, uint256 amount)")
|
||||
sighash The signature hash of the signature (4 bytes)
|
||||
inputs The description of the method input parameters
|
||||
outputs The description of the method output parameters
|
||||
payable Whether the method can accept *Ether*
|
||||
gas The maximum gas this method will consume (null if unknown)
|
||||
encode(params) A function which encodes *params*
|
||||
decode(data) A function which decodes the result *data*
|
||||
============================== ======================================
|
||||
|
||||
**Log Description**
|
||||
|
||||
============================== ======================================
|
||||
name description
|
||||
============================== ======================================
|
||||
name The event name (e.g. "Transfer")
|
||||
signature The event signature (e.g. "Transfer(address indexed,address indexed,uint256)")
|
||||
topics The event topics
|
||||
decode(data, topics) A function to parse the logs
|
||||
values The decoded values of the event
|
||||
============================== ======================================
|
||||
|
||||
**Transaction Description**
|
||||
|
||||
============================== ======================================
|
||||
name description
|
||||
============================== ======================================
|
||||
name The method name (e.g. "transfer")
|
||||
args The arguments passed to the method
|
||||
signature The method signature (e.g. "transfer(address to, uint256 amount)")
|
||||
sighash The signature hash of the signature (4 bytes)
|
||||
decode(data) A function to parse the result data
|
||||
value The value (in wei) of the transaction
|
||||
============================== ======================================
|
||||
|
||||
-----
|
||||
|
||||
Provider (Sub-Classing)
|
||||
=======================
|
||||
|
||||
See the :ref:`Provider API <api-provider>` for more common usage. This documentation
|
||||
is designed for developers that are sub-classing BaseProvider.
|
||||
|
||||
Static Methods
|
||||
--------------
|
||||
|
||||
:sup:`BaseProvider` . inherits ( childProvider ) |nbsp| :sup:`=>` |nbsp| :sup:`void`
|
||||
Set up *childProvider* as an provider, inheriting the parent prototype and
|
||||
set up a prototype.inherits on the *childProvider*.
|
||||
|
||||
Prototype
|
||||
---------
|
||||
|
||||
:sup:`prototype` . perform ( method , params ) |nbsp| :sup:`=>` |nbsp| :sup:`Promise<any>`
|
||||
The only method needed to override in a subclass. All values are sanitized
|
||||
and defaults populated in params and the result is sanitized before returning.
|
||||
Returns a :ref:`Promise <promise>`, see the example below for overview of
|
||||
*method* and *params*.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *BaseProvider Sub-Class Stub*
|
||||
|
||||
const ethers = require('ethers');
|
||||
|
||||
// The new provider Object
|
||||
function DemoProvider(something) {
|
||||
|
||||
let network = getNetworkSomehow()
|
||||
|
||||
// The super must be called with either a Network or a Promise
|
||||
// that resolves to a Network
|
||||
ethers.providers.BaseProvider.call(this, network);
|
||||
|
||||
ethers.utils.defineReadOnly(this, 'somethingElse', somethingElse);
|
||||
}
|
||||
|
||||
// Inherit the Provider
|
||||
ethers.providers.BaseProvider.inherits(DemoProvider);
|
||||
|
||||
// Override perform
|
||||
DemoProvider.prototype.perform = function(method, params) {
|
||||
switch (method) {
|
||||
case 'getBlockNumber':
|
||||
// Params:
|
||||
// { }
|
||||
|
||||
case 'getGasPrice':
|
||||
// Params:
|
||||
// { }
|
||||
|
||||
case 'getBalance':
|
||||
// Params:
|
||||
// {
|
||||
// address: address,
|
||||
// blockTag: blockTag
|
||||
// }
|
||||
|
||||
case 'getTransactionCount':
|
||||
// Params:
|
||||
// {
|
||||
// address: address,
|
||||
// blockTag: blockTag
|
||||
// }
|
||||
|
||||
case 'getCode':
|
||||
// Params:
|
||||
// {
|
||||
// address: address,
|
||||
// blockTag: blockTag
|
||||
// }
|
||||
|
||||
case 'getStorageAt':
|
||||
// Params:
|
||||
// {
|
||||
// address: address,
|
||||
// position: hexString,
|
||||
// blockTag: blockTag
|
||||
// }
|
||||
|
||||
case 'sendTransaction':
|
||||
// Params:
|
||||
// {
|
||||
// signedTransaction: hexString
|
||||
// }
|
||||
|
||||
case 'getBlock':
|
||||
// Params:
|
||||
// Exactly one of the following will be specified, the other will be absent
|
||||
// {
|
||||
// blockHash: blockHash,
|
||||
// blockTag: blockTag
|
||||
// }
|
||||
|
||||
case 'getTransaction':
|
||||
// Params:
|
||||
// {
|
||||
// transactionHash: hexString
|
||||
// }
|
||||
|
||||
case 'getTransactionReceipt':
|
||||
// Params:
|
||||
// {
|
||||
// transactionHash: hexString
|
||||
// }
|
||||
|
||||
case 'call':
|
||||
// Params:
|
||||
// {
|
||||
// transaction: See Transaction Requests (on Providers API)
|
||||
// }
|
||||
|
||||
case 'estimateGas':
|
||||
// Params:
|
||||
// {
|
||||
// transaction: See Transaction Requests (on Providers API)
|
||||
// }
|
||||
|
||||
case 'getLogs':
|
||||
// Params:
|
||||
// {
|
||||
// address: address,
|
||||
// fromBlock: blockTag,
|
||||
// toBlock: blockTag,
|
||||
// topics: array (possibly nested) of topics
|
||||
// }
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Promise.reject(new Error('not implemented - ' + method));
|
||||
};
|
||||
|
||||
-----
|
||||
|
||||
Recursive-Length Prefixed Encoding (RLP)
|
||||
========================================
|
||||
|
||||
This encoding method is used internally for several aspects of Ethereum, such as
|
||||
encoding transactions and determining contract addresses. For most developers this
|
||||
should not be necessary to use.
|
||||
|
||||
RLP can encode nested arrays, with data as :ref:`hex strings <hexstring>` and Uint8Array (or other non-Array
|
||||
:ref:`arrayish <arrayish>` objects). A decoded object will always have data represented as :ref:`hex strings <hexstring>` and
|
||||
Arrays.
|
||||
|
||||
See: https://github.com/ethereum/wiki/wiki/RLP
|
||||
|
||||
Static Methods
|
||||
--------------
|
||||
|
||||
:sup:`ethers . utils . RLP` . encode( object ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Encodes an object as an RLP :ref:`hex string <hexstring>`. (throws an Error if the object contains
|
||||
invalid items)
|
||||
|
||||
:sup:`ethers . utils . RLP` . decode( hexStringOrArrayish ) |nbsp| :sup:`=>` |nbsp| :sup:`any`
|
||||
Decode *hexStringOrArrayish* into the encoded object. (throws an Error if
|
||||
invalid RLP-coded data)
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *RLP coder*
|
||||
|
||||
let object = [ ["0x42"], "0x1234", [ [], [] ] ];
|
||||
|
||||
let encoded = ethers.utils.RLP.encode(object);
|
||||
console.log(encoded);
|
||||
// 0xc8c142821234c2c0c0
|
||||
|
||||
let decoded = ethers.utils.RLP.decode(encoded);
|
||||
console.log(decoded);
|
||||
// [ [ '0x42' ], '0x1234', [ [], [] ] ]
|
||||
|
||||
-----
|
||||
|
||||
Signing Key
|
||||
===========
|
||||
|
||||
The SigningKey interface provides an abstraction around the
|
||||
*secp256k1 elliptic curve cryptography* library, which signs digests,
|
||||
computes public keys from private keys and performs *ecrecover* which
|
||||
computes a public key from a digest and a signature.
|
||||
|
||||
|
||||
Creating Instances
|
||||
------------------
|
||||
|
||||
new :sup:`ethers . utils` . SigningKey ( privateKey )
|
||||
Create a new SigningKey and compute the corresponding public key and address.
|
||||
A private key may be a any :ref:`hex string <hexstring>` or an
|
||||
:ref:`Arrayish <arrayish>` representing 32 bytes.
|
||||
|
||||
|
||||
Prototype
|
||||
---------
|
||||
|
||||
:sup:`prototype` . address
|
||||
The Ethereum checksum address for this key pair.
|
||||
|
||||
:sup:`prototype` . privateKey
|
||||
The private key for the key pair.
|
||||
|
||||
:sup:`prototype` . publicKey
|
||||
The uncompressed public key for the key pair.
|
||||
|
||||
|
||||
Cryptographic Operations
|
||||
------------------------
|
||||
|
||||
:sup:`prototype` . signDigest ( messageDigest ) |nbsp| :sup:`=>` |nbsp| :sup:`Signature`
|
||||
The :ref:`expanded-format Signature <signature>` for the digests, signed
|
||||
by this key pair.
|
||||
|
||||
:sup:`prototype` . computeSharedSecret ( publicOrPrivateKey ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Compute the ECDH shared secret from this keys private key and the
|
||||
*publicOrPrivateKey*. In is generally considered good practice to
|
||||
further hash this value before using it as a key.
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Signing Key*
|
||||
|
||||
const ethers = require('ethers');
|
||||
|
||||
let privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
|
||||
let signingKey = new ethers.utils.SigningKey(privateKey);
|
||||
|
||||
console.log('Address: ' + signingKey.address);
|
||||
// "Address: 0x14791697260E4c9A71f18484C9f997B308e59325"
|
||||
|
||||
let message = "Hello World";
|
||||
let messageBytes = ethers.utils.toUtf8Bytes(message);
|
||||
let messageDigest = ethers.utils.keccak256(messageBytes);
|
||||
|
||||
console.log("Digest: " + messageDigest);
|
||||
// "Digest: 0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba"
|
||||
|
||||
let signature = signingKey.signDigest(messageDigest);
|
||||
|
||||
console.log(signature);
|
||||
// {
|
||||
// recoveryParam: 0,
|
||||
// r: "0x79f56f3422dc67f57b2aeeb0b20295a99ec90420b203177f83d419c98beda7fe",
|
||||
// s: "0x1a9d05433883bdc7e6d882740f4ea7921ef458a61b2cfe6197c2bb1bc47236fd"
|
||||
// }
|
||||
|
||||
let recovered = ethers.utils.recoverAddress(messageDigest, signature);
|
||||
|
||||
console.log("Recovered: " + recovered);
|
||||
// "Recovered: 0x14791697260E4c9A71f18484C9f997B308e59325"
|
||||
|
||||
let publicKey = signingKey.publicKey;
|
||||
|
||||
console.log('Public Key: ' + publicKey);
|
||||
// "Public Key: 0x026655feed4d214c261e0a6b554395596f1f1476a77d999560e5a8df9b8a1a3515"
|
||||
|
||||
let compressedPublicKey = ethers.utlis.computePublicKey(publicKey, true);
|
||||
let uncompressedPublicKey = ethers.utils.computePublicKey(publicKey, false);
|
||||
|
||||
console.log(compressedPublicKey);
|
||||
// "0x026655feed4d214c261e0a6b554395596f1f1476a77d999560e5a8df9b8a1a3515"
|
||||
|
||||
console.log(uncompressedPublicKey);
|
||||
// "0x046655feed4d214c261e0a6b554395596f1f1476a77d999560e5a8df9b8a1a35" +
|
||||
// "15217e88dd05e938efdd71b2cce322bf01da96cd42087b236e8f5043157a9c068e"
|
||||
|
||||
let address = ethers.utils.computeAddress(publicKey);
|
||||
|
||||
console.log('Address: ' + address);
|
||||
// "Address: 0x14791697260E4c9A71f18484C9f997B308e59325"
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _BIP 32 Specification: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||
.. _BIP 39 Specification: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
|
||||
|
||||
.. EOF
|
@ -1,760 +0,0 @@
|
||||
.. |nbsp| unicode:: U+00A0 .. non-breaking space
|
||||
|
||||
.. _api-contract:
|
||||
|
||||
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.
|
||||
|
||||
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 Contract:
|
||||
|
||||
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 (**no** *Ether* is required) to call. The result from them may also
|
||||
be returned to the caller.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Throughout this document, we will refer to the following Contract.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *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;
|
||||
}
|
||||
}
|
||||
|
||||
-----
|
||||
|
||||
.. _contract-deployment:
|
||||
|
||||
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.
|
||||
|
||||
Creating a Contract Factory
|
||||
---------------------------
|
||||
|
||||
new :sup:`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.
|
||||
|
||||
:sup:`ethers` . ContractFactory . fromSolidity ( compilerOutput [ , signer ] )
|
||||
Creates a ContractFactory from the *compilerOutput* of the *Solidity*
|
||||
compiler or from the *Truffle* JSON.
|
||||
(i.e. ``output.contracts['SimpleStorage.sol'].SimpleStorage``)
|
||||
|
||||
:sup:`prototype` . connect ( signer ) |nbsp| :sup:`=>` |nbsp| :sup:`ContractFactory`
|
||||
Create a **new instance** of the ContractFactory, connected to the new *signer*.
|
||||
|
||||
|
||||
Prototype
|
||||
---------
|
||||
|
||||
:sup:`prototype` . bytecode
|
||||
The Contract executable byte code..
|
||||
|
||||
:sup:`prototype` . interface
|
||||
The Contract Application Binary Interface (ABI).
|
||||
|
||||
:sup:`prototype` . signer
|
||||
The :ref:`Signer <signer>` that will be used to send transactions to the network.
|
||||
If this is null, ``deploy()`` cannot be called.
|
||||
|
||||
|
||||
Connecting
|
||||
----------
|
||||
|
||||
:sup:`prototype` . attach ( address ) |nbsp| :sup:`=>` |nbsp| :sup:`Contract`
|
||||
Connect to an existing instance of this Contract at *address* using the
|
||||
Contract Interface and Signer.
|
||||
|
||||
|
||||
Deployment
|
||||
----------
|
||||
|
||||
:sup:`prototype` . deploy ( ... ) |nbsp| :sup:`=>` |nbsp| :sup:`Promise<Contract>`
|
||||
Creates a transaction to deploy the transaction and
|
||||
sends it to the network using the contract :ref:`Signer <signer>`, returning a
|
||||
:ref:`Promise <promise>` that resolves to a Contract. The transaction is available
|
||||
as contract.deployTransaction.
|
||||
|
||||
Keep in mind that the Contract may not be mined immediately. The
|
||||
``contract.deployed()`` function will return a :ref:`Promise <promise>`
|
||||
which will resolve once the contract is deployed, or reject if there
|
||||
was an error during deployment.
|
||||
|
||||
:sup:`prototype` . getDeployTransaction ( ... ) |nbsp| :sup:`=>` |nbsp| :sup:`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.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Deploy a Contract*
|
||||
|
||||
const ethers = require('ethers');
|
||||
|
||||
// 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');
|
||||
|
||||
// Load the wallet to deploy the contract with
|
||||
let privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
|
||||
let wallet = new ethers.Wallet(privateKey, provider);
|
||||
|
||||
// Deployment is asynchronous, so we use an async IIFE
|
||||
(async function() {
|
||||
|
||||
// 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");
|
||||
|
||||
// The address the Contract WILL have once mined
|
||||
// See: https://ropsten.etherscan.io/address/0x2bd9aaa2953f988153c8629926d22a6a5f69b14e
|
||||
console.log(contract.address);
|
||||
// "0x2bD9aAa2953F988153c8629926D22A6a5F69b14E"
|
||||
|
||||
// The transaction that was sent to the network to deploy the Contract
|
||||
// See: https://ropsten.etherscan.io/tx/0x159b76843662a15bd67e482dcfbee55e8e44efad26c5a614245e12a00d4b1a51
|
||||
console.log(contract.deployTransaction.hash);
|
||||
// "0x159b76843662a15bd67e482dcfbee55e8e44efad26c5a614245e12a00d4b1a51"
|
||||
|
||||
// The contract is NOT deployed yet; we must wait until it is mined
|
||||
await contract.deployed()
|
||||
|
||||
// Done! The contract is deployed.
|
||||
})();
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Connecting to Existing Contracts
|
||||
=================================
|
||||
|
||||
Once a Contract has been deployed, it can be connected to using
|
||||
the **Contract** object.
|
||||
|
||||
Connecting to a Contract
|
||||
------------------------
|
||||
|
||||
new :sup:`ethers` . Contract ( addressOrName , abi , providerOrSigner )
|
||||
Connects to the contract at *addressOrName* defined by *abi*, connected as *providerOrSigner*.
|
||||
|
||||
For supported formats for *abi*, see :ref:`Contract ABI <contract-abi>`.
|
||||
|
||||
For access capabilities and restrictions, see :ref:`Providers vs Signers <providers-vs-signers>`
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Connecting to an existing Contract*
|
||||
|
||||
const ethers = require('ethers');
|
||||
|
||||
// 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 = ethers.getDefaultProvider();
|
||||
|
||||
// The address from the above deployment example
|
||||
let contractAddress = "0x2bD9aAa2953F988153c8629926D22A6a5F69b14E";
|
||||
|
||||
// We connect to the Contract using a Provider, so we will only
|
||||
// have read-only access to the Contract
|
||||
let contract = new ethers.Contract(contractAddress, abi, provider);
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Calling a read-only Constant Method*
|
||||
|
||||
// Get the current value
|
||||
let currentValue = await contract.getValue();
|
||||
|
||||
console.log(currentValue);
|
||||
// "Hello World"
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Calling a Non-Constant Method*
|
||||
|
||||
// A Signer from a private key
|
||||
let privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
|
||||
let wallet = new ethers.Wallet(privateKey, provider);
|
||||
|
||||
// Create a new instance of the Contract with a Signer, which allows
|
||||
// update methods
|
||||
let contractWithSigner = contract.connect(wallet);
|
||||
// ... OR ...
|
||||
// let contractWithSigner = new Contract(contractAddress, abi, wallet)
|
||||
|
||||
// Set a new Value, which returns the transaction
|
||||
let tx = await contractWithSigner.setValue("I like turtles.");
|
||||
|
||||
// See: https://ropsten.etherscan.io/tx/0xaf0068dcf728afa5accd02172867627da4e6f946dfb8174a7be31f01b11d5364
|
||||
console.log(tx.hash);
|
||||
// "0xaf0068dcf728afa5accd02172867627da4e6f946dfb8174a7be31f01b11d5364"
|
||||
|
||||
// The operation is NOT complete yet; we must wait until it is mined
|
||||
await tx.wait();
|
||||
|
||||
// Call the Contract's getValue() method again
|
||||
let newValue = await contract.getValue();
|
||||
|
||||
console.log(currentValue);
|
||||
// "I like turtles."
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Listening to Events*
|
||||
|
||||
contract.on("ValueChanged", (author, oldValue, newValue, event) => {
|
||||
// Called when anyone changes the value
|
||||
|
||||
console.log(author);
|
||||
// "0x14791697260E4c9A71f18484C9f997B308e59325"
|
||||
|
||||
console.log(oldValue);
|
||||
// "Hello World"
|
||||
|
||||
console.log(newValue);
|
||||
// "Ilike turtles."
|
||||
|
||||
// See Event Emitter below for all properties on Event
|
||||
console.log(event.blockNumber);
|
||||
// 4115004
|
||||
});
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Filtering an Events*
|
||||
|
||||
// A filter that matches my Signer as the author
|
||||
let filter = contract.filters.ValueChanged(wallet.address);
|
||||
|
||||
contract.on(filter, (author, oldValue, newValue, event) => {
|
||||
// Called ONLY when your account changes the value
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
Prototype
|
||||
---------
|
||||
|
||||
:sup:`prototype` . address
|
||||
The address (or ENS name) of the contract.
|
||||
|
||||
:sup:`prototype` . deployTransaction
|
||||
If the contract was deployed by a ContractFactory, this is the transaction
|
||||
used to deploy it, otherwise it is null.
|
||||
|
||||
:sup:`prototype` . interface
|
||||
The :ref:`Interface <api-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 :ref:`Contract Meta-Class <contract-metaclass>`.
|
||||
|
||||
-----
|
||||
|
||||
Waiting for Deployment
|
||||
----------------------
|
||||
|
||||
:sup:`prototype` . deployed ( ) |nbsp| :sup:`=>` |nbsp| :sup:`Promise<Contract>`
|
||||
If the contract is the result of ``deploy()``, returns
|
||||
a :ref:`Promise <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
|
||||
:ref:`Promise <promise>` that resolves once the on-chain code has
|
||||
been confirmed.
|
||||
|
||||
-----
|
||||
|
||||
.. _contract-metaclass:
|
||||
|
||||
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 :ref:`Contract ABI <contract-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()``.
|
||||
|
||||
:sup:`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 :ref:`Provider <provider-connect>` or
|
||||
a Signer with a :ref:`Provider <provider-connect>`.
|
||||
|
||||
Calling a **Non-Constant** function (i.e. sending a transaction) requires a
|
||||
:ref:`Signer <signer>`.
|
||||
|
||||
:sup:`prototype` . estimate . *functionName*
|
||||
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.
|
||||
|
||||
:sup:`prototype` . filters . *eventname*
|
||||
A function that generates filters that can be listened to, using the
|
||||
``on(eventName, ...)`` function, filtered by the Event values.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Filtering Events*
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _contract-overrides:
|
||||
|
||||
Overrides
|
||||
=========
|
||||
|
||||
Every Contract method may take one additional (optional) parameter which specifies the
|
||||
transaction (or call) overrides.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Contract Transaction 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)
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Contract Call 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)
|
||||
|
||||
-----
|
||||
|
||||
.. _contract-event-emitter:
|
||||
|
||||
Event Emitter
|
||||
=============
|
||||
|
||||
Each Contract supports many of the operations available from the `Event Emitter API`_.
|
||||
|
||||
To listen for Events, the contract requires either a :ref:`Provider <provider-connect>` or
|
||||
a Signer with a :ref:`Provider <provider-connect>`.
|
||||
|
||||
|
||||
Event Names
|
||||
-----------
|
||||
|
||||
The available eventNames are:
|
||||
|
||||
- **string** -- The name of an event (e.g. "TestEvent" or "TestEvent(string, uint)")
|
||||
- **filter** -- See :ref:`Contract Filters <contract-filter>`
|
||||
- **\*** -- All events
|
||||
|
||||
|
||||
Event Object
|
||||
------------
|
||||
|
||||
All event callbacks receive the parameters specified in the ABI as well as one additional
|
||||
Event Object with
|
||||
|
||||
- **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
|
||||
------------------
|
||||
|
||||
:sup:`prototype` . on ( eventName , callback ) |nbsp| :sup:`=>` |nbsp| :sup:`Contract`
|
||||
Registers *callback* to be called on every *eventName*. Returns the contract, so calls may be chained.
|
||||
|
||||
:sup:`prototype` . addListner ( eventName , callback ) |nbsp| :sup:`=>` |nbsp| :sup:`Contract`
|
||||
An alias for ``on``.
|
||||
|
||||
:sup:`prototype` . once ( eventName , callback ) |nbsp| :sup:`=>` |nbsp| :sup:`Contract`
|
||||
Register *callback* to be called at most once, for *eventName*. Returns the contract, so calls may be chained.
|
||||
|
||||
:sup:`prototype` . emit ( eventName , ... ) |nbsp| :sup:`=>` |nbsp| :sup:`boolean`
|
||||
Trigger all callbacks for *eventName*, returning true if there was at
|
||||
least one listener. This should generally not be called directly.
|
||||
|
||||
:sup:`prototype` . listenerCount ( [ eventName ] ) |nbsp| :sup:`=>` |nbsp| :sup:`number`
|
||||
Returns the number of callbacks registered for *eventName*.
|
||||
|
||||
:sup:`prototype` . listeners ( eventName ) |nbsp| :sup:`=>` |nbsp| :sup:`Listeners[]`
|
||||
Returns a list of callbacks for *eventName*.
|
||||
|
||||
:sup:`prototype` . removeAllListeners ( eventName ) |nbsp| :sup:`=>` |nbsp| :sup:`Contract`
|
||||
De-registers all listeners for *eventName*. Returns the contract, so calls may be chained.
|
||||
|
||||
:sup:`prototype` . removeListener ( eventName , callback ) |nbsp| :sup:`=>` |nbsp| :sup:`Contract`
|
||||
De-registers the specific *callback* for *eventName*. Returns the contract, so calls may be chained.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Events*
|
||||
|
||||
contract.on("ValueChanged", (oldValue, newValue, event) => {
|
||||
console.log(oldValue, newValue);
|
||||
});
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _providers-vs-signers:
|
||||
|
||||
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 **providerOrSigner** parameter when connecting to a Contract.
|
||||
|
||||
There are three possible cases for connecting a Contract using the providerOrSigner.
|
||||
|
||||
============================================ ========================================
|
||||
providerOrSigner Operation Privileges
|
||||
============================================ ========================================
|
||||
:ref:`Provider <provider-connect>` Read-Only Access
|
||||
:ref:`Signer <signer>` (without a provider) Write-Only Access (as account owner)
|
||||
:ref:`Signer <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.
|
||||
|
||||
:sup:`prototype` . connect ( providerOrSigner )
|
||||
Create a **new instance** of the Contract object connected as *providerOrSigner*.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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 <arrayish>`.
|
||||
|
||||
To convert the string into an array, use the :ref:`arrayify() <arrayish>` utility function.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Strings
|
||||
-------
|
||||
|
||||
For short strings, many Contracts use a bytes32 to encode a null-terminated
|
||||
string representation, rather than a length-prefixed representation, so the
|
||||
:ref:`formatBytes32String <bytes32string>` and :ref:`parseBytes32String <bytes32string>`
|
||||
utility functions can be used to handle this conversion.
|
||||
|
||||
To convert between the two dynamic types, strings and bytes, the
|
||||
:ref:`toUtf8Bytes() <utf8-strings>` and :ref:`toUtf8String() <utf8-strings>`
|
||||
utility functions can be used.
|
||||
|
||||
|
||||
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**.
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Example Return Types*
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _contract-filter:
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *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.filters.Transfer(null, myAddress);
|
||||
|
||||
// Listen for our filtered results
|
||||
contract.on(filter, (from, to, value) => {
|
||||
console.log('I received ' + value.toString() + ' tokens from ' + from);
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
.. _contract-abi:
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Human-Readable ABI*
|
||||
|
||||
let ABI = [
|
||||
"event Transfer(address from, address to, uint amount)",
|
||||
"function transfer(address to, uint amount)",
|
||||
"function symbol() view returns (string)"
|
||||
]
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _Human-Readable ABI: https://blog.ricmoo.com/human-readable-contract-abis-in-ethers-js-141902f4d917
|
||||
.. _Event Emitter API: https://nodejs.org/api/events.html#events_class_eventemitter
|
||||
|
||||
.. EOF
|
File diff suppressed because it is too large
Load Diff
@ -1,701 +0,0 @@
|
||||
.. |nbsp| unicode:: U+00A0 .. non-breaking space
|
||||
|
||||
Utilities
|
||||
*********
|
||||
|
||||
The utility functions provide a large assortment of common utility functions
|
||||
required to write dapps, process user input and format data.
|
||||
|
||||
-----
|
||||
|
||||
Addresses
|
||||
=========
|
||||
|
||||
There are :ref:`several formats <checksum-address>` available to represent Ethereum
|
||||
addresses and various ways they are determined.
|
||||
|
||||
.. _utils-getaddress:
|
||||
|
||||
:sup:`utils` . getAddress ( address ) |nbsp| :sup:`=>` |nbsp| :sup:`Address`
|
||||
Normalize any supported address-format to a :ref:`checksum address <checksum-address>`.
|
||||
|
||||
:sup:`utils` . getIcapAddress ( address ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Normalize any supported address-format to a :ref:`ICAP address <icap-address>`.
|
||||
|
||||
:sup:`utils` . getContractAddress ( transaction ) |nbsp| :sup:`=>` |nbsp| :sup:`Address`
|
||||
Computes the contract address of a contract deployed by *transaction*. The only
|
||||
properties used are *from* and *nonce*.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *convert between address formats*
|
||||
|
||||
let address = "0xd115bffabbdd893a6f7cea402e7338643ced44a6";
|
||||
let icapAddress = "XE93OF8SR0OWI6F4FO88KWO4UNNGG1FEBHI";
|
||||
|
||||
console.log(utils.getAddress(address));
|
||||
// "0xD115BFFAbbdd893A6f7ceA402e7338643Ced44a6"
|
||||
|
||||
console.log(utils.getAddress(icapAddress));
|
||||
// "0xD115BFFAbbdd893A6f7ceA402e7338643Ced44a6"
|
||||
|
||||
console.log(utils.getAddress(address, true));
|
||||
// "XE93OF8SR0OWI6F4FO88KWO4UNNGG1FEBHI"
|
||||
|
||||
console.log(utils.getAddress(icapAddress, true));
|
||||
// "XE93OF8SR0OWI6F4FO88KWO4UNNGG1FEBHI"
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *determine a contract address*
|
||||
|
||||
// Ropsten: 0x5bdfd14fcc917abc2f02a30721d152a6f147f09e8cbaad4e0d5405d646c5c3e1
|
||||
let transaction = {
|
||||
from: '0xc6af6e1a78a6752c7f8cd63877eb789a2adb776c',
|
||||
nonce: 0
|
||||
};
|
||||
|
||||
console.log(utils.getContractAddress(transaction));
|
||||
// "0x0CcCC7507aEDf9FEaF8C8D731421746e16b4d39D"
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _arrayish:
|
||||
|
||||
Arrayish
|
||||
========
|
||||
|
||||
An arrayish object is used to describe binary data and has the following conditions met:
|
||||
|
||||
- has a *length* property
|
||||
- has a value for each index from 0 up to (but excluding) *length*
|
||||
- has a valid byte for each value; a byte is an integer in the range [0, 255]
|
||||
- is **not** a string
|
||||
|
||||
**Examples:** ``Buffer``, ``[ 1, 2, 3 ]``, ``Uint8Array``
|
||||
|
||||
:sup:`utils` . isArrayish ( object ) |nbsp| :sup:`=>` |nbsp| :sup:`boolean`
|
||||
Returns true if *object* can be treated as an arrayish object.
|
||||
|
||||
:sup:`utils` . arrayify ( hexStringOrBigNumberOrArrayish ) |nbsp| :sup:`=>` |nbsp| :sup:`Uint8Array`
|
||||
Returns a Uint8Array of a hex string, BigNumber or of an `Arrayish`_ object.
|
||||
|
||||
:sup:`utils` . concat ( arrayOfHexStringsAndArrayish ) |nbsp| :sup:`=>` |nbsp| :sup:`Uint8Array`
|
||||
Return a Uint8Array of all *arrayOfHexStringsAndArrayish* concatenated.
|
||||
|
||||
:sup:`utils` . padZeros ( typedUint8Array, length ) |nbsp| :sup:`=>` |nbsp| :sup:`Uint8Array`
|
||||
Return a Uint8Array of *typedUint8Array* with zeros prepended to *length* bytes.
|
||||
|
||||
:sup:`utils` . stripZeros ( hexStringOrArrayish ) |nbsp| :sup:`=>` |nbsp| :sup:`Uint8Array`
|
||||
Returns a Uint8Array with all leading zero **bytes** striped.
|
||||
|
||||
-----
|
||||
|
||||
.. _bignumber:
|
||||
|
||||
Big Numbers
|
||||
===========
|
||||
|
||||
A BigNumber is an immutable object which allow accurate math operations
|
||||
on values larger than :ref:`JavaScript can accurately handle <ieee754>`
|
||||
can safely handle. Also see: :ref:`Constants <constants>`
|
||||
|
||||
:sup:`prototype` . add ( otherValue ) |nbsp| :sup:`=>` |nbsp| :sup:`BigNumber`
|
||||
Return a new BigNumber of this plus *otherValue*.
|
||||
|
||||
:sup:`prototype` . sub ( otherValue ) |nbsp| :sup:`=>` |nbsp| :sup:`BigNumber`
|
||||
Return a new BigNumber of this minus *otherValue*.
|
||||
|
||||
:sup:`prototype` . mul ( otherValue ) |nbsp| :sup:`=>` |nbsp| :sup:`BigNumber`
|
||||
Return a new BigNumber of this times *otherValue*.
|
||||
|
||||
:sup:`prototype` . div ( otherValue ) |nbsp| :sup:`=>` |nbsp| :sup:`BigNumber`
|
||||
Return a new BigNumber of this divided by *otherValue*.
|
||||
|
||||
:sup:`prototype` . mod ( otherValue ) |nbsp| :sup:`=>` |nbsp| :sup:`BigNumber`
|
||||
Return a new BigNumber of this modulo *otherValue*.
|
||||
|
||||
:sup:`prototype` . maskn ( bits ) |nbsp| :sup:`=>` |nbsp| :sup:`BigNumber`
|
||||
Return a new BigNumber with the number of *bits* masked.
|
||||
|
||||
:sup:`prototype` . eq ( otherValue ) |nbsp| :sup:`=>` |nbsp| :sup:`boolean`
|
||||
Return true if this is equal to *otherValue*.
|
||||
|
||||
:sup:`prototype` . lt ( otherValue ) |nbsp| :sup:`=>` |nbsp| :sup:`boolean`
|
||||
Return true if this is less than *otherValue*.
|
||||
|
||||
:sup:`prototype` . lte ( otherValue ) |nbsp| :sup:`=>` |nbsp| :sup:`boolean`
|
||||
Return true if this is less or equal to *otherValue*.
|
||||
|
||||
:sup:`prototype` . gt ( otherValue ) |nbsp| :sup:`=>` |nbsp| :sup:`boolean`
|
||||
Return true if this is greater than *otherValue*.
|
||||
|
||||
:sup:`prototype` . gte ( otherValue ) |nbsp| :sup:`=>` |nbsp| :sup:`boolean`
|
||||
Return true if this is greater than or equal to *otherValue*.
|
||||
|
||||
:sup:`prototype` . isZero ( ) |nbsp| :sup:`=>` |nbsp| :sup:`boolean`
|
||||
Return true if this is equal to zero.
|
||||
|
||||
:sup:`prototype` . toNumber ( ) |nbsp| :sup:`=>` |nbsp| :sup:`number`
|
||||
Return a JavaScript number of the value.
|
||||
|
||||
An error is thrown if the value is outside the safe range for JavaScript
|
||||
IEEE 754 64-bit floating point numbers (over 53 bits of mantissa).
|
||||
|
||||
:sup:`prototype` . toString () |nbsp| :sup:`=>` |nbsp| :sup:`string`
|
||||
Return a decimal string representation.
|
||||
|
||||
:sup:`prototype` . toHexString ( ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Return a hexstring representation of the value.
|
||||
|
||||
|
||||
Creating Instances
|
||||
------------------
|
||||
|
||||
:sup:`utils` . bigNumberify ( value ) |nbsp| :sup:`=>` |nbsp| :sup:`BigNumber`
|
||||
Returns a BigNumber instance of *value*. The *value* may be anything that can
|
||||
reliably be converted into a BigNumber:
|
||||
|
||||
============================ ======================= =================================
|
||||
Type Examples Notes
|
||||
============================ ======================= =================================
|
||||
decimal string ``"42"``, ``"-42"``
|
||||
hexadecimal string ``"0x2a"``, ``"-0x2a"`` case-insensitive
|
||||
numbers ``42``, ``-42`` must be witin the `safe range`_
|
||||
:ref:`Arrayish <arrayish>` ``[ 30, 252 ]`` big-endian encoding
|
||||
BigNumber any other BigNumber returns the same instance
|
||||
============================ ======================= =================================
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *examples*
|
||||
|
||||
let gasPriceWei = utils.bigNumberify("20902747399");
|
||||
let gasLimit = utils.bigNumberify(3000000);
|
||||
|
||||
let maxCostWei = gasPriceWei.mul(gasLimit)
|
||||
console.log("Max Cost: " + maxCostWei.toString());
|
||||
// "Max Cost: 62708242197000000"
|
||||
|
||||
console.log("Number: " + maxCostWei.toNumber());
|
||||
// throws an Error, the value is too large for JavaScript to handle safely
|
||||
|
||||
-----
|
||||
|
||||
.. _bytes32string:
|
||||
|
||||
Bytes32 Strings
|
||||
===============
|
||||
|
||||
Often for short strings, it is far more efficient to store them as
|
||||
a fixed, null-terminated bytes32, instead of a dynamic length-prefixed
|
||||
bytes.
|
||||
|
||||
:sup:`utils` . formatBytes32String ( text ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Returns a :ref:`hex string <hexstring>` representation of *text*, exactly
|
||||
32 bytes wide. Strings **must** be 31 bytes or shorter, or an exception
|
||||
is thrown.
|
||||
|
||||
**NOTE:** Keep in mind that UTF-8 characters outside the ASCII range can
|
||||
be multiple bytes long.
|
||||
|
||||
:sup:`utils` . parseBytes32String ( hexStringOrArrayish ) |nbsp| :sup:`=>` |nbsp| :sup:`string`
|
||||
Returns *hexStringOrArrayish* as the original string, as generated by ``formatBytes32String``.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *example*
|
||||
|
||||
let text = "Hello World!"
|
||||
|
||||
let bytes32 = ethers.utils.formatBytes32String(text)
|
||||
// "0x48656c6c6f20576f726c64210000000000000000000000000000000000000000"
|
||||
|
||||
let originalText = ethers.utils.parseBytes32String(bytes32)
|
||||
// "Hello World!"
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _constants:
|
||||
|
||||
Constants
|
||||
=========
|
||||
|
||||
:sup:`ethers . constants` . AddressZero
|
||||
The address ``0x0000000000000000000000000000000000000000``.
|
||||
|
||||
:sup:`ethers . constants` . HashZero
|
||||
The bytes32 ``0x0000000000000000000000000000000000000000000000000000000000000000``.
|
||||
|
||||
:sup:`ethers . constants` . MaxUint256
|
||||
The bytes32 ``0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff``.
|
||||
|
||||
:sup:`ethers . constants` . NegativeOne
|
||||
The :ref:`BigNumber <bignumber>` ``bigNumberify(-1)``.
|
||||
|
||||
:sup:`ethers . constants` . Zero
|
||||
The :ref:`BigNumber <bignumber>` ``bigNumberify(0)``.
|
||||
|
||||
:sup:`ethers . constants` . One
|
||||
The :ref:`BigNumber <bignumber>` ``bigNumberify(1)``.
|
||||
|
||||
:sup:`ethers . constants` . Two
|
||||
The :ref:`BigNumber <bignumber>` ``bigNumberify(2)``.
|
||||
|
||||
:sup:`ethers . constants` . WeiPerEther
|
||||
The :ref:`BigNumber <bignumber>` ``bigNumberify("1000000000000000000")``.
|
||||
|
||||
:sup:`ethers . constants` . EtherSymbol
|
||||
The Greek character Xi, used as the symbol for *ether*.
|
||||
|
||||
-----
|
||||
|
||||
Cryptographic Functions
|
||||
=======================
|
||||
|
||||
Elliptic Curve
|
||||
--------------
|
||||
|
||||
:sup:`utils` . computeAddress ( publicOrPrivateKey ) |nbsp| :sup:`=>` |nbsp| :sup:`Address`
|
||||
Computes the Ethereum address given a public key or private key.
|
||||
|
||||
:sup:`utils` . computePublicKey ( publicOrPrivateKey [ , compressed :sup:`= false` ] ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Compute the public key for *publicOrPrivateKey*, optionally *compressed*. If
|
||||
*publicOrPrivateKey* is a public key, it may be either compressed or uncompressed.
|
||||
|
||||
:sup:`utils` . recoverAddress ( digest , signature ) |nbsp| :sup:`=>` |nbsp| :sup:`Address`
|
||||
Returns the Ethereum address by using ecrecover with the *digest* for the
|
||||
*signature*.
|
||||
|
||||
:sup:`utils` . recoverPublicKey ( digest , signature ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Returns the public key by using ecrecover with the *digest* for the *signature*.
|
||||
|
||||
:sup:`utils` . verifyMessage ( messageStringOrArrayish , signature ) |nbsp| :sup:`=>` |nbsp| :sup:`Addresss`
|
||||
Returns the address of the account that signed *messageStringOrArrayish* to
|
||||
generate *signature*.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *verify a message signature*
|
||||
|
||||
let signature = "0xddd0a7290af9526056b4e35a077b9a11b513aa0028ec6c9880948544508f3c63" +
|
||||
"265e99e47ad31bb2cab9646c504576b3abc6939a1710afc08cbf3034d73214b8" +
|
||||
"1c";
|
||||
|
||||
let signingAddress = Wallet.verifyMessage('hello world', signature);
|
||||
|
||||
console.log(signingAddress);
|
||||
// "0x14791697260E4c9A71f18484C9f997B308e59325"
|
||||
|
||||
Hash Functions
|
||||
--------------
|
||||
|
||||
:sup:`utils` . keccak256 ( hexStringOrArrayish ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Compute the keccak256 cryptographic hash of a value, returned as a hex string. (Note:
|
||||
often Ethereum documentation refers to this, **incorrectly**, as SHA3)
|
||||
|
||||
:sup:`utils` . sha256 ( hexStringOrArrayish ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Compute the SHA2-256 cryptographic hash of a value, returned as a hex string.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *hashing binary data*
|
||||
|
||||
console.log(utils.keccak256([ 0x42 ]));
|
||||
// '0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111'
|
||||
|
||||
console.log(utils.keccak256("0x42"));
|
||||
// '0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111'
|
||||
|
||||
|
||||
console.log(utils.sha256([ 0x42 ]));
|
||||
// '0xdf7e70e5021544f4834bbee64a9e3789febc4be81470df629cad6ddb03320a5c'
|
||||
|
||||
console.log(utils.sha256("0x42"));
|
||||
// '0xdf7e70e5021544f4834bbee64a9e3789febc4be81470df629cad6ddb03320a5c'
|
||||
|
||||
|
||||
Hash Function Helpers
|
||||
---------------------
|
||||
|
||||
:sup:`utils` . hashMessage ( stringOrArrayish ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Compute the prefixed message hash of a stringOrArrayish, by converting the
|
||||
message to bytes (as necessary) and prefixing with ``\x19Ethereum Signed Message\n``
|
||||
and the length of the message. See the `eth_sign`_ JSON-RPC method for more information.
|
||||
|
||||
:sup:`utils` . id ( utf8String ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Compute the keccak256 cryptographic hash of a UTF-8 string, returned as a hex string.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *hashing utf-8 strings*
|
||||
|
||||
// Convert the string to binary data
|
||||
let message = "Hello World";
|
||||
let messageBytes = utils.toUtf8Bytes(message);
|
||||
utils.keccak256(messageBytes);
|
||||
// '0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba'
|
||||
|
||||
// Which is equivalent to using the id function
|
||||
utils.id("Hello World");
|
||||
// '0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba'
|
||||
|
||||
|
||||
// Compute the sighash for a Solidity method
|
||||
console.log(utils.id("addr(bytes32)"));
|
||||
// '0x3b3b57de213591bb50e06975ea011e4c8c4b3e6de4009450c1a9e55f66e4bfa4'
|
||||
|
||||
Key Derivation
|
||||
--------------
|
||||
|
||||
:sup:`utils` . pbkdf2 ( password , salt , iterations , keylen , hashAlgorithm )
|
||||
Return the pbkdf2 derived key from *password* and *salt* with *iterations* of
|
||||
*length* using the *hashAlgorithm*. The supported hash algorithms are ``sha256``
|
||||
and ``sha512``.
|
||||
|
||||
Random
|
||||
------
|
||||
|
||||
:sup:`utils` . randomBytes ( length ) |nbsp| :sup:`=>` |nbsp| :sup:`Uint8Array`
|
||||
Return a Uint8Array of cryptographically secure random bytes
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *generate random bytes*
|
||||
|
||||
let randomBytes3 = utils.randomBytes(3)
|
||||
// Uint8Array [ 194, 22, 140 ]
|
||||
|
||||
let randomBytes32 = utils.randomBytes(32)
|
||||
// Uint8Array [ 162, 131, 117, 110, 196, 73, 144, 177, 201, 75, 88,
|
||||
// 105, 227, 210, 104, 226, 82, 65, 103, 157, 36, 170,
|
||||
// 214, 92, 190, 141, 239, 54, 96, 39, 240, 95 ]
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *generate a random number*
|
||||
|
||||
let randomNumber = utils.bigNumberify(utils.randomBytes(32));
|
||||
// BigNumber { _hex: 0x617542634156966e0bbb6c673bf88015f542c96eb115186fd93881518f05f7ff }
|
||||
|
||||
Solidity
|
||||
--------
|
||||
|
||||
Solidity uses a `non-standard packed mode`_ to encode parameters that are passed
|
||||
into its hashing functions. The parameter types and values can be used to compute
|
||||
the result of the hash functions as would be performed by Solidity.
|
||||
|
||||
:sup:`utils` . solidityKeccak256 ( types, values ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Compute the keccak256 cryptographic hash using the Solidity non-standard (tightly)
|
||||
packed data for *values* given the *types*.
|
||||
|
||||
:sup:`utils` . soliditySha256 ( types, values ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Compute the SHA256 cryptographic hash using the Solidity non-standard (tightly)
|
||||
packed data for *values* given the *types*.
|
||||
|
||||
:sup:`utils` . solidityPack ( types, values ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Compute the Solidity non-standard (tightly) packed data for *values* given the *types*.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *examples*
|
||||
|
||||
let result = utils.solidityKeccak256([ 'int8', 'bytes1', 'string' ], [ -1, '0x42', 'hello' ]);
|
||||
console.log(result);
|
||||
// '0x52d7e6a62ca667228365be2143375d0a2a92a3bd4325dd571609dfdc7026686e'
|
||||
|
||||
result = utils.soliditySha256([ 'int8', 'bytes1', 'string' ], [ -1, '0x42', 'hello' ]);
|
||||
console.log(result);
|
||||
// '0x1eaebba7999af2691d823bf0c817e635bbe7e89ec7ed32a11e00ca94e86cbf37'
|
||||
|
||||
result = utils.solidityPack([ 'int8', 'bytes1', 'string' ], [ -1, '0x42', 'hello' ]);
|
||||
console.log(result);
|
||||
// '0xff4268656c6c6f'
|
||||
|
||||
-----
|
||||
|
||||
|
||||
Ether Strings and Wei
|
||||
=====================
|
||||
|
||||
:sup:`utils` . etherSymbol
|
||||
The ethereum symbol (the Greek letter *Xi* )
|
||||
|
||||
.. _parseEther:
|
||||
|
||||
:sup:`utils` . parseEther ( etherString ) |nbsp| :sup:`=>` |nbsp| :sup:`BigNumber`
|
||||
Parse the *etherString* representation of ether into a BigNumber instance
|
||||
of the amount of wei.
|
||||
|
||||
.. _formatEther:
|
||||
|
||||
:sup:`utils` . formatEther ( wei ) |nbsp| :sup:`=>` |nbsp| :sup:`string`
|
||||
Format an amount of *wei* into a decimal string representing the amount of ether.
|
||||
The output will always include at least one whole number and at least one decimal
|
||||
place, otherwise leading and trailing 0's will be trimmed.
|
||||
|
||||
.. _parseUnits:
|
||||
|
||||
:sup:`utils` . parseUnits ( valueString , decimalsOrUnitName ) |nbsp| :sup:`=>` |nbsp| :sup:`BigNumber`
|
||||
Parse the *valueString* representation of units into a BigNumber instance
|
||||
of the amount of wei. The *decimalsOrUnitsName* may be a number of decimals between
|
||||
3 and 18 (multiple of 3) or a name, such as `gwei`.
|
||||
|
||||
.. _formatUnits:
|
||||
|
||||
:sup:`utils` . formatUnits ( wei , decimalsOrUnitName ) |nbsp| :sup:`=>` |nbsp| :sup:`string`
|
||||
Format an amount of *wei* into a decimal string representing the amount of units.
|
||||
The output will always include at least one whole number and at least one decimal place,
|
||||
otherwise leading and trailing 0's will be trimmed. The *decimalsOrUnitsName*
|
||||
may be a number of decimals between 3 and 18 (multiple of 3) or a name, such as `gwei`.
|
||||
|
||||
:sup:`utils` . commify ( numberOrString ) |nbsp| :sup:`=>` |nbsp| :sup:`string`
|
||||
Returns *numberOrString* with commas placed at every third position within the whole
|
||||
component. If *numberOrString* contains a decimal point, the output will as well with
|
||||
at least one digit for both the whole and decimal components. If there no decimal,
|
||||
then the output will also not contain a decimal.
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *examples*
|
||||
|
||||
let wei = utils.parseEther('1000.0');
|
||||
console.log(wei.toString(10));
|
||||
// "1000000000000000000000"
|
||||
|
||||
console.log(utils.formatEther(0));
|
||||
// "0.0"
|
||||
|
||||
let wei = utils.bigNumberify("1000000000000000000000");
|
||||
|
||||
console.log(utils.formatEther(wei));
|
||||
// "1000.0"
|
||||
|
||||
console.log(utils.formatEther(wei, {commify: true}));
|
||||
// "1,000.0"
|
||||
|
||||
console.log(utils.formatEther(wei, {pad: true}));
|
||||
// "1000.000000000000000000" (18 decimal places)
|
||||
|
||||
console.log(utils.formatEther(wei, {commify: true, pad: true}));
|
||||
// "1,000.000000000000000000" (18 decimal places)
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _hexstring:
|
||||
|
||||
Hex Strings
|
||||
===========
|
||||
|
||||
A hex string is **always** prefixed with "0x" and consists of the characters
|
||||
0 -- 9 and a -- f. It is always returned lower case with even-length, but any hex
|
||||
string passed into a function may be any case and may be odd-length.
|
||||
|
||||
:sup:`utils` . hexlify ( numberOrBigNumberOrHexStringOrArrayish ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Converts any number, :ref:`BigNumber <bignumber>`, hex string or
|
||||
`Arrayish`_ to a hex string. (otherwise, throws an error)
|
||||
|
||||
:sup:`utils` . isHexString ( value ) |nbsp| :sup:`=>` |nbsp| :sup:`boolean`
|
||||
Returns true if *value* is a valid hexstring.
|
||||
|
||||
:sup:`utils` . hexDataLength ( hexString ) |nbsp| :sup:`=>` |nbsp| :sup:`number`
|
||||
Returns the length (in bytes) of *hexString* if it is a valid data hexstring (even length).
|
||||
|
||||
:sup:`utils` . hexDataSlice ( hexString , offset [ , endOffset ] ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Returns a string for the subdata of *hexString* from *offset* **bytes**
|
||||
(each byte is two nibbled) to *endOffset* **bytes**. If no *endOffset* is
|
||||
specified, the result is to the end of the *hexString* is used. Each byte is two nibbles.
|
||||
|
||||
:sup:`utils` . hexStripZeros ( hexString ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Returns *hexString* with all leading zeros removed, but retaining at least
|
||||
one nibble, even if zero (e.g. ``0x0``). This may return an odd-length string.
|
||||
|
||||
:sup:`utils` . hexZeroPad ( hexString , length ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Returns *hexString* padded (on the left) with zeros to length **bytes** (each
|
||||
byte is two nibbles).
|
||||
|
||||
-----
|
||||
|
||||
Namehash
|
||||
========
|
||||
|
||||
:sup:`utils` . namehash ( ensName ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Compute the namehash of *ensName*. Currently only supports the
|
||||
characters ``[a-z0-9.-]``. The concerns with fully supporting UTF-8
|
||||
are largely security releated, but `are open for discussion`_.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *examples*
|
||||
|
||||
let namehash = utils.namehash('ricmoo.firefly.eth');
|
||||
// "0x0bcad17ecf260d6506c6b97768bdc2acfb6694445d27ffd3f9c1cfbee4a9bd6d"
|
||||
|
||||
-----
|
||||
|
||||
.. _signature:
|
||||
|
||||
Signatures
|
||||
==========
|
||||
|
||||
There are two common formats for signatures in Ethereum. The **flat-format**, which
|
||||
is a hexstring with 65 bytes (130 nibbles); or an **expanded-format**, which is an object with
|
||||
the properties:
|
||||
|
||||
- **r** and **s** --- the (r, s) public point of a signature
|
||||
- **recoveryParam** --- the recovery parameter of a signautre (either ``0`` or ``1``)
|
||||
- **v** --- the recovery param nomalized for Solidity (either ``27`` or ``28``)
|
||||
|
||||
:sup:`utils` . splitSignature ( hexStringOrArrayishOrSignature ) |nbsp| :sup:`=>` |nbsp| :sup:`Signature`
|
||||
Returns an expanded-format signature object for *hexStringOrArrayishOrSignature*.
|
||||
Passing in an signature that is already in the expanded-format will ensure
|
||||
both *recoveryParam* and *v* are populated.
|
||||
|
||||
:sup:`utils` . joinSignature ( signature ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Returns the flat-format signature hexstring of *signature*. The final *v*
|
||||
byte will always be normalized to ``0x1b`` of ``0x1c``.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *To Expanded-Format*
|
||||
|
||||
// Flat-format; this is the format provided by JSON-RPC responses
|
||||
let flat = "0x0ba9770fd8778383f6d56faadc71e17b75f0d6e3ff0a408d5e6c4cee3bd70a16" +
|
||||
"3574da0ebfb1eaac261698b057b342e52ea53f85287272cea471a4cda41e3466" +
|
||||
"1b"
|
||||
let expanded = utils.splitSignature(flat);
|
||||
|
||||
console.log(expanded);
|
||||
// {
|
||||
// r: "0x0ba9770fd8778383f6d56faadc71e17b75f0d6e3ff0a408d5e6c4cee3bd70a16",
|
||||
// s: "0x3574da0ebfb1eaac261698b057b342e52ea53f85287272cea471a4cda41e3466",
|
||||
// recoveryParam: 0,
|
||||
// v: 27
|
||||
// }
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *To Flat-Format*
|
||||
|
||||
// Expanded-format; this is the format Solidity and other tools often require
|
||||
let expanded = {
|
||||
r: "0x0ba9770fd8778383f6d56faadc71e17b75f0d6e3ff0a408d5e6c4cee3bd70a16",
|
||||
s: "0x3574da0ebfb1eaac261698b057b342e52ea53f85287272cea471a4cda41e3466",
|
||||
recoveryParam: 0,
|
||||
v: 27
|
||||
}
|
||||
let flat = utils.joinSignature(expanded);
|
||||
|
||||
console.log(flat)
|
||||
// "0x0ba9770fd8778383f6d56faadc71e17b75f0d6e3ff0a408d5e6c4cee3bd70a16" +
|
||||
// "3574da0ebfb1eaac261698b057b342e52ea53f85287272cea471a4cda41e3466" +
|
||||
// "1b"
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _transactions:
|
||||
|
||||
Transactions
|
||||
============
|
||||
|
||||
:sup:`utils` . serializeTransaction ( transaction [ , signature ] ) |nbsp| :sup:`=>` |nbsp| :sup:`hex`
|
||||
Serialize *transaction* as a :ref:`hex-string <hexstring>`, optionally including
|
||||
the *signature*.
|
||||
|
||||
If *signature* is provided, it may be either the :ref:`Flat Format <signature>`
|
||||
or the :ref:`Expanded Format <signature>`, and the serialized transaction will
|
||||
be a signed transaction.
|
||||
|
||||
:sup:`utils` . parseTransaction ( rawTransaction ) |nbsp| :sup:`=>` |nbsp| :sup:`Transaction`
|
||||
Parse the serialized transaction, returning an object with the properties:
|
||||
|
||||
- **to**
|
||||
- **nonce**
|
||||
- **gasPrice**
|
||||
- **gasLimit**
|
||||
- **data**
|
||||
- **value**
|
||||
- **chainId**
|
||||
|
||||
If the transactions is signed, addition properties will be present:
|
||||
|
||||
- **r**, **s** and **v** --- the signature public point and recoveryParam (adjusted for the chainId)
|
||||
- **from** --- the address of the account that signed the transaction
|
||||
- **hash** --- the transaction hash
|
||||
|
||||
-----
|
||||
|
||||
.. _utf8-strings:
|
||||
|
||||
UTF-8 Strings
|
||||
=============
|
||||
|
||||
.. _utf8-to-bytes:
|
||||
|
||||
:sup:`utils` . toUtf8Bytes ( string ) |nbsp| :sup:`=>` |nbsp| :sup:`Uint8Array`
|
||||
Converts a UTF-8 string to a Uint8Array.
|
||||
|
||||
.. _utf8-to-string:
|
||||
|
||||
:sup:`utils` . toUtf8String ( hexStringOrArrayish , [ ignoreErrors :sup:`= false` ) |nbsp| :sup:`=>` |nbsp| :sup:`string`
|
||||
Converts a hex-encoded string or array to its UTF-8 representation.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *To UTF-8 Bytes*
|
||||
|
||||
let text = "Hello World";
|
||||
|
||||
let bytes = utils.toUtf8Bytes(text);
|
||||
|
||||
console.log(bytes);
|
||||
// Uint8Array [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *To UTF-8 String*
|
||||
|
||||
let array = [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100];
|
||||
|
||||
let stringFromArray = utils.toUtf8String(array)
|
||||
|
||||
console.log(stringFromArray);
|
||||
// "Hello World"
|
||||
|
||||
let hexString = "0x48656c6c6f20576f726c64";
|
||||
let stringFromHexString = utils.toUtf8String(hexString);
|
||||
|
||||
console.log(stringFromHexString);
|
||||
// "Hello World"
|
||||
|
||||
-----
|
||||
|
||||
Web
|
||||
===
|
||||
|
||||
:sup:`utils` . fetchJson ( urlOrInfo [ , processFunc ] ) |nbsp| :sup:`=>` |nbsp| :sup:`Promise<any>`
|
||||
Returns a :ref:`Promise <promise>` of the contents of *urlOrInfo*, processed by
|
||||
*processFunc*.
|
||||
|
||||
The *urlOrInfo* may also be specified as an object with the properties:
|
||||
|
||||
- **url** --- the JSON-RPC URL (required)
|
||||
- **user** --- a username to use for Basic Authentication (optional)
|
||||
- **password** --- a password to use for Basic Authentication (optional)
|
||||
- **allowInsecure** --- allow Basic Authentication over an insecure HTTP network (default: false)
|
||||
- **timeout** --- number of milliseconds to abort the request (default: 2 minutes)
|
||||
- **headers** --- additional headers to send to the server (case insensitive)
|
||||
|
||||
:sup:`utils` . poll ( func , [ options ] ) |nbsp| :sup:`=>` |nbsp| :sup:`Promise<any>`
|
||||
Poll using the function *func*, resolving when it does not return ``undefined``. By
|
||||
default this method will use the `exponential back-off`_ algorithm.
|
||||
|
||||
The *options* is an object with the properties:
|
||||
|
||||
- **timeout** --- after this many millisecconds, the promise will reject with a ``timeout`` error (default: no timeout)
|
||||
- **floor** --- minimum amount of time between polling (default: 0)
|
||||
- **ceiling** --- minimum amount of time between polling (default: 10s)
|
||||
- **interval** --- the interval to use for exponential backoff (default: 250ms)
|
||||
- **onceBlock** --- a function which takes 2 parameters, the string ``block`` and a callback *func*; polling will occur everytime *func* is called; any provider can be passed in for this property
|
||||
|
||||
-----
|
||||
|
||||
|
||||
.. _are open for discussion: https://github.com/ethers-io/ethers.js/issues/42
|
||||
.. _eth_sign: https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
|
||||
.. _exponential back-off: https://en.wikipedia.org/wiki/Exponential_backoff
|
||||
.. _safe range: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger
|
||||
.. _non-standard packed mode: http://solidity.readthedocs.io/en/develop/abi-spec.html#non-standard-packed-mode
|
||||
|
||||
.. EOF
|
@ -1,459 +0,0 @@
|
||||
.. |nbsp| unicode:: U+00A0 .. non-breaking space
|
||||
|
||||
.. _api-wallet:
|
||||
|
||||
Wallets and Signers
|
||||
*******************
|
||||
|
||||
A **Wallet** manages a private/public key pair which is used to cryptographically sign
|
||||
transactions and prove ownership on the Ethereum network.
|
||||
|
||||
-----
|
||||
|
||||
.. _wallet:
|
||||
|
||||
Wallet
|
||||
======
|
||||
|
||||
The **Wallet** implements the :ref:`Signer API <signer>` and can be used anywhere a *Signer*
|
||||
is expected and has all the required properties.
|
||||
|
||||
|
|
||||
|
||||
Creating Instances
|
||||
------------------
|
||||
|
||||
new :sup:`Wallet` ( privateKey [ , provider ] )
|
||||
Creates a new instance from *privateKey* and optionally connect a provider
|
||||
|
||||
:sup:`Wallet` . createRandom ( [ options ] ) |nbsp| :sup:`=>` |nbsp| :sup:`Wallet`
|
||||
Creates a new random wallet. Ensure this wallet is stored somewhere safe, if
|
||||
lost there is **NO way to recover it**.
|
||||
|
||||
Options may have the properties:
|
||||
|
||||
- **extraEntropy** --- additional entropy to stir into the random source
|
||||
|
||||
.. _fromEncryptedJson:
|
||||
|
||||
:sup:`Wallet` . fromEncryptedJson ( json, password [ , progressCallback ] ) |nbsp| :sup:`=>` |nbsp| :sup:`Wallet`
|
||||
Decrypt an encrypted Secret Storage `JSON Wallet`_ (from Geth, parity, Crowdsale
|
||||
tools, or that was created using *prototype.encrypt* )
|
||||
|
||||
:sup:`Wallet` . fromMnemonic ( mnemonic [ , path :sup:`= "m/44'/60'/0'/0/0"` [ , wordlist ] ] ) |nbsp| :sup:`=>` |nbsp| :sup:`Wallet`
|
||||
Generate a `BIP-039`_ + `BIP-044`_ wallet from *mnemonic* deriving *path* using
|
||||
the *wordlist*. The default language is English (en).
|
||||
|
||||
In the browserified ``dist/ethers.min.js`` only the English wordlist is
|
||||
available. Each additional wordlist may be included by adding a ``<script>``
|
||||
for the ``dist/wordlist-*.js``
|
||||
|
||||
The current supported wordlists are:
|
||||
|
||||
===================== =========================== =======================
|
||||
Language node.js Browser
|
||||
===================== =========================== =======================
|
||||
English (US) ``ethers.wordlists.en`` *included*
|
||||
Italian ``ethers.wordlists.it`` ``dist/wordlist-it.js``
|
||||
Japanese ``ethers.wordlists.ja`` ``dist/wordlist-ja.js``
|
||||
Korean ``ethers.wordlists.ko`` ``dist/wordlist-ko.js``
|
||||
Chinese (simplified) ``ethers.wordlists.zh_cn`` ``dist/wordlist-zh.js``
|
||||
Chinese (traditional) ``ethers.wordlists.zh_tw`` ``dist/wordlist-zh.js``
|
||||
===================== =========================== =======================
|
||||
|
||||
.. _wallet-connect:
|
||||
|
||||
:sup:`prototype` . connect ( provider ) |nbsp| :sup:`=>` |nbsp| :sup:`Wallet`
|
||||
Creates a new Wallet instance from an existing instance, connected to a new *provider*.
|
||||
|
||||
|
|
||||
|
||||
|
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *load a private key*
|
||||
|
||||
let privateKey = "0x0123456789012345678901234567890123456789012345678901234567890123";
|
||||
let wallet = new ethers.Wallet(privateKey);
|
||||
|
||||
// Connect a wallet to mainnet
|
||||
let provider = ethers.getDefaultProvider();
|
||||
let walletWithProvider = new ethers.Wallet(privateKey, provider);
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *create a new random account*
|
||||
|
||||
let randomWallet = ethers.Wallet.createRandom();
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *load a JSON wallet*
|
||||
|
||||
let data = {
|
||||
id: "fb1280c0-d646-4e40-9550-7026b1be504a",
|
||||
address: "88a5c2d9919e46f883eb62f7b8dd9d0cc45bc290",
|
||||
Crypto: {
|
||||
kdfparams: {
|
||||
dklen: 32,
|
||||
p: 1,
|
||||
salt: "bbfa53547e3e3bfcc9786a2cbef8504a5031d82734ecef02153e29daeed658fd",
|
||||
r: 8,
|
||||
n: 262144
|
||||
},
|
||||
kdf: "scrypt",
|
||||
ciphertext: "10adcc8bcaf49474c6710460e0dc974331f71ee4c7baa7314b4a23d25fd6c406",
|
||||
mac: "1cf53b5ae8d75f8c037b453e7c3c61b010225d916768a6b145adf5cf9cb3a703",
|
||||
cipher: "aes-128-ctr",
|
||||
cipherparams: {
|
||||
iv: "1dcdf13e49cea706994ed38804f6d171"
|
||||
}
|
||||
},
|
||||
"version" : 3
|
||||
};
|
||||
|
||||
let json = JSON.stringify(data);
|
||||
let password = "foo";
|
||||
|
||||
ethers.Wallet.fromEncryptedJson(json, password).then(function(wallet) {
|
||||
console.log("Address: " + wallet.address);
|
||||
// "Address: 0x88a5C2d9919e46F883EB62F7b8Dd9d0CC45bc290"
|
||||
});
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *load a mnemonic phrase*
|
||||
|
||||
let mnemonic = "radar blur cabbage chef fix engine embark joy scheme fiction master release";
|
||||
let mnemonicWallet = ethers.Wallet.fromMnemonic(mnemonic);
|
||||
|
||||
// Load the second account from a mnemonic
|
||||
let path = "m/44'/60'/1'/0/0";
|
||||
let secondMnemonicWallet = ethers.Wallet.fromMnemonic(mnemonic, path);
|
||||
|
||||
// Load using a non-english locale wordlist (the path "null" will use the default)
|
||||
let secondMnemonicWallet = ethers.Wallet.fromMnemonic(mnemonic, null, ethers.wordlists.ko);
|
||||
|
||||
-----
|
||||
|
||||
Prototype
|
||||
---------
|
||||
|
||||
:sup:`prototype` . address
|
||||
The public address of a wallet
|
||||
|
||||
:sup:`prototype` . privateKey
|
||||
The private key of a wallet; keep this secret
|
||||
|
||||
:sup:`prototype` . provider
|
||||
A connected :ref:`Provider <provider>` which allows the wallet to
|
||||
connect to the Ethereum network to query its state and send transactions,
|
||||
or null if no provider is connected.
|
||||
|
||||
To change the provider, use the :ref:`connect <wallet-connect>` method, which will return
|
||||
a **new instance** of the Wallet connected to the provider.
|
||||
|
||||
|
||||
|
||||
:sup:`prototype` . mnemonic
|
||||
The mnemonic phrase for this wallet, or null if the mnemonic is unknown.
|
||||
|
||||
:sup:`prototype` . path
|
||||
The mnemonic path for this wallet, or null if the mnemonic is unknown.
|
||||
|
||||
-----
|
||||
|
||||
Signing
|
||||
-------
|
||||
|
||||
:sup:`prototype` . sign ( transaction ) |nbsp| :sup:`=>` |nbsp| :sup:`Promise<string>`
|
||||
Signs *transaction* and returns a :ref:`Promise <promise>` that resolves to
|
||||
the signed transaction as a :ref:`hex string <hexstring>`.
|
||||
|
||||
In general, the `sendTransaction`_ method is preferred to ``sign``, as it can automatically
|
||||
populate values asynchronously.
|
||||
|
||||
The properties for transaction are all optional and include:
|
||||
|
||||
- **to**
|
||||
- **gasLimit**
|
||||
- **gasPrice**
|
||||
- **nonce**
|
||||
- **data**
|
||||
- **value**
|
||||
- **chainId**
|
||||
|
||||
:sup:`prototype` . signMessage ( message ) |nbsp| :sup:`=>` |nbsp| :sup:`Promise<string>`
|
||||
Signs *message* and returns a :ref:`Promise <promise>` that resolves to
|
||||
the :ref:`flat-format <signature>` signature.
|
||||
|
||||
If *message* is a string, it is converted to UTF-8 bytes, otherwise it is
|
||||
preserved as a binary representation of the :ref:`Arrayish <arrayish>` data.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *signing transactions*
|
||||
|
||||
let privateKey = "0x3141592653589793238462643383279502884197169399375105820974944592"
|
||||
let wallet = new ethers.Wallet(privateKey)
|
||||
|
||||
console.log(wallet.address)
|
||||
// "0x7357589f8e367c2C31F51242fB77B350A11830F3"
|
||||
|
||||
// All properties are optional
|
||||
let transaction = {
|
||||
nonce: 0,
|
||||
gasLimit: 21000,
|
||||
gasPrice: utils.bigNumberify("20000000000"),
|
||||
|
||||
to: "0x88a5C2d9919e46F883EB62F7b8Dd9d0CC45bc290",
|
||||
// ... or supports ENS names
|
||||
// to: "ricmoo.firefly.eth",
|
||||
|
||||
value: utils.parseEther("1.0"),
|
||||
data: "0x",
|
||||
|
||||
// This ensures the transaction cannot be replayed on different networks
|
||||
chainId: ethers.utils.getNetwork('homestead').chainId
|
||||
}
|
||||
|
||||
let signPromise = wallet.sign(transaction)
|
||||
|
||||
signPromise.then((signedTransaction) => {
|
||||
|
||||
console.log(signedTransaction);
|
||||
// "0xf86c808504a817c8008252089488a5c2d9919e46f883eb62f7b8dd9d0cc45bc2
|
||||
// 90880de0b6b3a76400008025a05e766fa4bbb395108dc250ec66c2f88355d240
|
||||
// acdc47ab5dfaad46bcf63f2a34a05b2cb6290fd8ff801d07f6767df63c1c3da7
|
||||
// a7b83b53cd6cea3d3075ef9597d5"
|
||||
|
||||
// This can now be sent to the Ethereum network
|
||||
let provider = ethers.getDefaultProvider()
|
||||
provider.sendTransaction(signedTransaction).then((tx) => {
|
||||
|
||||
console.log(tx);
|
||||
// {
|
||||
// // These will match the above values (excluded properties are zero)
|
||||
// "nonce", "gasLimit", "gasPrice", "to", "value", "data", "chainId"
|
||||
//
|
||||
// // These will now be present
|
||||
// "from", "hash", "r", "s", "v"
|
||||
// }
|
||||
// Hash:
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *signing text messages*
|
||||
|
||||
let privateKey = "0x3141592653589793238462643383279502884197169399375105820974944592"
|
||||
let wallet = new ethers.Wallet(privateKey);
|
||||
|
||||
// Sign a text message
|
||||
let signPromise = wallet.signMessage("Hello World!")
|
||||
|
||||
signPromise.then((signature) => {
|
||||
|
||||
// Flat-format
|
||||
console.log(signature);
|
||||
// "0xea09d6e94e52b48489bd66754c9c02a772f029d4a2f136bba9917ab3042a0474
|
||||
// 301198d8c2afb71351753436b7e5a420745fed77b6c3089bbcca64113575ec3c
|
||||
// 1c"
|
||||
|
||||
// Expanded-format
|
||||
console.log(ethers.utils.splitSignature(signature));
|
||||
// {
|
||||
// r: "0xea09d6e94e52b48489bd66754c9c02a772f029d4a2f136bba9917ab3042a0474",
|
||||
// s: "0x301198d8c2afb71351753436b7e5a420745fed77b6c3089bbcca64113575ec3c",
|
||||
// v: 28,
|
||||
// recoveryParam: 1
|
||||
// }
|
||||
});
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *signing binary messages*
|
||||
|
||||
let privateKey = "0x3141592653589793238462643383279502884197169399375105820974944592"
|
||||
let wallet = new ethers.Wallet(privateKey);
|
||||
|
||||
// The 66 character hex string MUST be converted to a 32-byte array first!
|
||||
let hash = "0x3ea2f1d0abf3fc66cf29eebb70cbd4e7fe762ef8a09bcc06c8edf641230afec0";
|
||||
let binaryData = ethers.utils.arrayify(hash);
|
||||
|
||||
let signPromise = wallet.signMessage(binaryData)
|
||||
|
||||
signPromise.then((signature) => {
|
||||
|
||||
console.log(signature);
|
||||
// "0x5e9b7a7bd77ac21372939d386342ae58081a33bf53479152c87c1e787c27d06b
|
||||
// 118d3eccff0ace49891e192049e16b5210047068384772ba1fdb33bbcba58039
|
||||
// 1c"
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
Blockchain Operations
|
||||
---------------------
|
||||
|
||||
These operations require the wallet have a provider attached to it.
|
||||
|
||||
:sup:`prototype` . getBalance ( [ blockTag :sup:`= "latest"` ] ) |nbsp| :sup:`=>` |nbsp| :sup:`Promise<BigNumber>`
|
||||
Returns a :ref:`Promise <promise>` that resolves to the balance of the wallet
|
||||
(as a :ref:`BigNumber <bignumber>`, in **wei**) at the :ref:`blockTag <blocktag>`.
|
||||
|
||||
:sup:`prototype` . getTransactionCount ( [ blockTag :sup:`= "latest"` ] ) |nbsp| :sup:`=>` |nbsp| :sup:`Promise<number>`
|
||||
Returns a :ref:`Promise <promise>` that resovles to the number of transactions
|
||||
this account has ever sent (also called the *nonce*) at the :ref:`blockTag <blocktag>`.
|
||||
|
||||
:sup:`prototype` . estimateGas ( transaction ) |nbsp| :sup:`=>` |nbsp| :sup:`Promise<BigNumber>`
|
||||
Returns a :ref:`Promise <promise>` with the estimated cost for *transaction* (as a
|
||||
:ref:`BigNumber <bignumber>`, in **gas**)
|
||||
|
||||
.. _sendTransaction:
|
||||
|
||||
:sup:`prototype` . sendTransaction ( transaction ) |nbsp| :sup:`=>` |nbsp| :sup:`Promise<TransactionResponse>`
|
||||
Sends the *transaction* (see :ref:`Transaction Requests <transaction-request>`) to
|
||||
the network and returns a :ref:`Promise <promise>` that resolves to a
|
||||
:ref:`Transaction Response <transaction-response>`. Any properties that are not
|
||||
provided will be populated from the network.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *query the network*
|
||||
|
||||
// We require a provider to query the network
|
||||
let provider = ethers.getDefaultProvider();
|
||||
|
||||
let privateKey = "0x3141592653589793238462643383279502884197169399375105820974944592"
|
||||
let wallet = new ethers.Wallet(privateKey, provider);
|
||||
|
||||
let balancePromise = wallet.getBalance();
|
||||
|
||||
balancePromise.then((balance) => {
|
||||
console.log(balance);
|
||||
});
|
||||
|
||||
let transactionCountPromise = wallet.getTransactionCount();
|
||||
|
||||
transactionCountPromise.then((transactionCount) => {
|
||||
console.log(transactionCount);
|
||||
});
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *transfer ether*
|
||||
|
||||
// We require a provider to send transactions
|
||||
let provider = ethers.getDefaultProvider();
|
||||
|
||||
let privateKey = "0x3141592653589793238462643383279502884197169399375105820974944592"
|
||||
let wallet = new ethers.Wallet(privateKey, provider);
|
||||
|
||||
let amount = ethers.utils.parseEther('1.0');
|
||||
|
||||
let tx = {
|
||||
to: "0x88a5c2d9919e46f883eb62f7b8dd9d0cc45bc290",
|
||||
// ... or supports ENS names
|
||||
// to: "ricmoo.firefly.eth",
|
||||
|
||||
// We must pass in the amount as wei (1 ether = 1e18 wei), so we
|
||||
// use this convenience function to convert ether to wei.
|
||||
value: ethers.utils.parseEther('1.0')
|
||||
};
|
||||
|
||||
let sendPromise = wallet.sendTransaction(tx);
|
||||
|
||||
sendPromise.then((tx) => {
|
||||
console.log(tx);
|
||||
// {
|
||||
// // All transaction fields will be present
|
||||
// "nonce", "gasLimit", "pasPrice", "to", "value", "data",
|
||||
// "from", "hash", "r", "s", "v"
|
||||
// }
|
||||
});
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Encrypted JSON Wallets
|
||||
----------------------
|
||||
|
||||
Many systems store private keys as encrypted JSON wallets, in various formats. There are several
|
||||
formats and algorithms that are used, all of which are supported to be read.
|
||||
Only the secure scrypt variation can be generated.
|
||||
|
||||
See :ref:`Wallet.fromEncryptedJson <fromEncryptedJson>` for creating a
|
||||
Wallet instance from a JSON wallet.
|
||||
|
||||
:sup:`prototype` . encrypt ( password [ , options [ , progressCallback ] ] ) |nbsp| :sup:`=>` |nbsp| :sup:`Promise<string>`
|
||||
Encrypts the wallet as an encrypted JSON wallet, with the *password*.
|
||||
|
||||
All options are optional. The valid options are:
|
||||
|
||||
- **salt** --- the salt to use for scrypt
|
||||
- **iv** --- the initialization vecotr to use for aes-ctr-128
|
||||
- **uuid** --- the UUID to use for the wallet
|
||||
- **scrypt** --- the scrypt parameters to use (N, r and p)
|
||||
- **entropy** --- the mnemonic entropy of this wallet; generally you should **not** specify this
|
||||
- **mnemonic** --- the mnemonic phrase of this wallet; generally you should **not** specify this
|
||||
- **path** --- the mnemonic path of this wallet; generally you should **not** specify this
|
||||
|
||||
If the *progressCallback* is specified, it will be called periodically during
|
||||
encryption with a value between 0 and 1, inclusive indicating the progress.
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *encrypt a wallet as an encrypted JSON wallet*
|
||||
|
||||
let password = "password123";
|
||||
|
||||
function callback(progress) {
|
||||
console.log("Encrypting: " + parseInt(progress * 100) + "% complete");
|
||||
}
|
||||
|
||||
let encryptPromise = wallet.encrypt(password, callback);
|
||||
|
||||
encryptPromise.then(function(json) {
|
||||
console.log(json);
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
.. _signer:
|
||||
|
||||
Signer API
|
||||
==========
|
||||
|
||||
The Signer API is an abstract class which makes it easy to extend and add new signers,
|
||||
that can be used by this library and extension libraries. The :ref:`Wallet <wallet>`
|
||||
extends the Signer API, as do the :ref:`JsonRpcSigner <signer-jsonrpc>` and the
|
||||
`Ledger Hardware Wallet Signer`_.
|
||||
|
||||
To implement a Signer, inherit the abstract class *ethers.types.Signer* and implement
|
||||
the following properties:
|
||||
|
||||
:sup:`object` . provider
|
||||
A :ref:`Provider <api-provider>` that is connected to the network. This is optional, however,
|
||||
without a *provider*, **only** *write-only* operations should be expected to work.
|
||||
|
||||
:sup:`object` . getAddress ( ) |nbsp| :sup:`=>` |nbsp| :sup:`Promise<Address>`
|
||||
Returns a :ref:`Promise <promise>` that resolves to the account address.
|
||||
|
||||
:sup:`object` . signMessage ( message ) |nbsp| :sup:`=>` |nbsp| :sup:`Promise<hex>`
|
||||
Returns a :ref:`Promise <promise>` that resolves to the :ref:`Flat-Format Signature <signature>`
|
||||
for the *message*.
|
||||
|
||||
If *message* is a string, it is converted to UTF-8 bytes, otherwise it is
|
||||
preserved as a binary representation of the :ref:`Arrayish <arrayish>` data.
|
||||
|
||||
:sup:`object` . sendTransaction ( transaction ) |nbsp| :sup:`=>` |nbsp| :sup:`Promise<TransactionResponse>`
|
||||
Sends the *transaction* (see :ref:`Transaction Requests <transaction-request>`) to
|
||||
the network and returns a :ref:`Promise <promise>` that resolves to a
|
||||
:ref:`Transaction Response <transaction-response>`. Any properties that are not
|
||||
provided will be populated from the network.
|
||||
|
||||
-----
|
||||
|
||||
.. _BIP-039: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
|
||||
.. _BIP-044: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
||||
.. _Ledger Hardware Wallet Signer: https://github.com/ethers-io/ethers-ledger
|
||||
.. _JSON Wallet: https://medium.com/@julien.maffre/what-is-an-ethereum-keystore-file-86c8c5917b97
|
||||
.. EOF
|
@ -1,16 +0,0 @@
|
||||
.. _api:
|
||||
|
||||
Application Programming Interface (API)
|
||||
***************************************
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api-wallet
|
||||
api-providers
|
||||
api-contract
|
||||
api-utils
|
||||
|
||||
-----
|
||||
|
||||
.. EOF
|
@ -1,221 +0,0 @@
|
||||
Accounts
|
||||
********
|
||||
|
||||
Some quick examples of using Accounts.
|
||||
|
||||
-----
|
||||
|
||||
Dump All JSON Wallet Balances (in current directory)
|
||||
====================================================
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Load JSON Wallet Balances*
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const ethers = require('ethers');
|
||||
|
||||
let provider = ethers.getDefaultProvider();
|
||||
|
||||
// Geth
|
||||
let dirname = path.join(process.env.HOME, '.ethereum', 'keystore');
|
||||
|
||||
// Parity (use the name of your chain for chainDirectory, such as "homestead")
|
||||
//var dirname = path.join(process.env.HOME, '.parity', 'keys', chainDirectory, 'keys');
|
||||
|
||||
let filenames = fs.readdirSync(dirname);
|
||||
|
||||
filenames.forEach(async function(filename) {
|
||||
|
||||
// Get the Wallet JSON
|
||||
let data = await fs.readFile(path.join(dirname,filename));
|
||||
|
||||
// Get the Wallet address
|
||||
let address = ethers.utils.getJsonWalletAddress(data.toString());
|
||||
|
||||
// Look up the balance
|
||||
let balance = await provider.getBalance(address);
|
||||
|
||||
console.log(address + ':' + ethers.utils.formatEther(balance));
|
||||
});
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Sweep an Account into Another
|
||||
=============================
|
||||
|
||||
This will sweep **all** the funds from one account's *privateKey* and
|
||||
place it in another account, *newAddress*.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Sweep an Account*
|
||||
|
||||
const ethers = require('ethers');
|
||||
|
||||
async function sweep(privateKey, newAddress) {
|
||||
|
||||
let provider = ethers.getDefaultProvider();
|
||||
|
||||
let wallet = new ethers.Wallet(privateKey, provider);
|
||||
|
||||
// Make sure we are sweeping to an EOA, not a contract. The gas required
|
||||
// to send to a contract cannot be certain, so we may leave dust behind
|
||||
// or not set a high enough gas limit, in which case the transaction will
|
||||
// fail.
|
||||
let code = await provider.getCode(newAddress);
|
||||
if (code !== '0x') { throw new Error('Cannot sweep to a contract'); }
|
||||
|
||||
// Get the current balance
|
||||
let balance = await wallet.getBalance();
|
||||
|
||||
// Normally we would let the Wallet populate this for us, but we
|
||||
// need to compute EXACTLY how much value to send
|
||||
let gasPrice = await provider.getGasPrice();
|
||||
|
||||
// The exact cost (in gas) to send to an Externally Owned Account (EOA)
|
||||
let gasLimit = 21000;
|
||||
|
||||
// The balance less exactly the txfee in wei
|
||||
let value = balance.sub(gasPrice.mul(gasLimit))
|
||||
|
||||
let tx = await wallet.sendTransaction({
|
||||
gasLimit: gasLimit,
|
||||
gasPrice: gasPrice,
|
||||
to: newAddress,
|
||||
value: value
|
||||
});
|
||||
|
||||
console.log('Sent in Transaction: ' + tx.hash);
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
Coalesce Jaxx Wallets
|
||||
=====================
|
||||
|
||||
The Jaxx Wallet (for iOS, Android, desktop, et cetera) uses HD wallets on Ethereum the
|
||||
same way as Bitcoin, which results in each transaction being received by a separate
|
||||
address. As a result, funds get spread across many accounts, making several operations
|
||||
in Ethereum impossible.
|
||||
|
||||
This short recipe will coalesce all these accounts into a single one, by sending the funds
|
||||
from each account into a single one.
|
||||
|
||||
This also results in paying multiple transaction fees (1 fee per account to merge).
|
||||
|
||||
@TODO: This is incomplete!!
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *TODO*
|
||||
|
||||
const ethers = require('ethers');
|
||||
|
||||
let provider = ethers.getDefaultProvider();
|
||||
|
||||
let mnemonic = "radar blur cabbage chef fix engine embark joy scheme fiction master release";
|
||||
let hdnode = ethers.utils.HDNode.fromMnemonic(mnemonic);
|
||||
let hdnode = hdnode.derivePath("m/44'/60'/0'/0");
|
||||
|
||||
// @TODO
|
||||
|
||||
-----
|
||||
|
||||
Access Funds in a Mnemonic Phrase Wallet
|
||||
========================================
|
||||
|
||||
@TODO: This is incomplete
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *TODO*
|
||||
|
||||
const ethers = require('ethers');
|
||||
|
||||
let walletPath = {
|
||||
"standard": "m/44'/60'/0'/0/0",
|
||||
|
||||
// @TODO: Include some non-standard wallet paths
|
||||
};
|
||||
|
||||
let mnemonic = "radar blur cabbage chef fix engine embark joy scheme fiction master release";
|
||||
let hdnode = ethers.HDNode.fromMnemonic(mnemonic);
|
||||
let node = hdnode.derivePath(walletPath.standard);
|
||||
|
||||
let wallet = new ethers.Wallet(node.privateKey);
|
||||
console.log(wallet.address);
|
||||
// 0xaC39b311DCEb2A4b2f5d8461c1cdaF756F4F7Ae9
|
||||
|
||||
// @TODO:
|
||||
|
||||
-----
|
||||
|
||||
Random Mnemonic
|
||||
===============
|
||||
|
||||
Often you may simply want a random mnemonic that is valid. It is important to
|
||||
note that **not** all random sets of words are valid; there is a checksum
|
||||
included in the binary encoding of the entropy, so it is important to use
|
||||
a method that correctly encodes this checksum.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Random Mnemonic*
|
||||
|
||||
const ethers = require('ethers');
|
||||
|
||||
// All createRandom Wallets are generated from random mnemonics
|
||||
let wallet = ethers.Wallet.createRandom();
|
||||
let randomMnemonic = wallet.mnemonic;
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *More Complex Random Mnemonic*
|
||||
|
||||
const utils = require('ethers/utils');
|
||||
|
||||
// Chose the length of your mnemonic:
|
||||
// - 16 bytes => 12 words (* this example)
|
||||
// - 20 bytes => 15 words
|
||||
// - 24 bytes => 18 words
|
||||
// - 28 bytes => 21 words
|
||||
// - 32 bytes => 24 words
|
||||
let bytes = ethers.utils.random(16);
|
||||
|
||||
// Select the language:
|
||||
// - en, es, fr, ja, ko, it, zh_ch, zh_tw
|
||||
let language = ethers.wordlists.en;
|
||||
|
||||
let randomMnemonic = ethers.utils.HDNode.entropyToMnemonic(bytes, language)
|
||||
|
||||
-----
|
||||
|
||||
Get Transaction History
|
||||
=======================
|
||||
|
||||
Unfortunately, transaction history is not something that is easy to get. It
|
||||
is not indexed by the blockchain, not by a standard node, "out-of-the-box".
|
||||
At the time of this recipe, the indices to store the entire history are
|
||||
around 800GB. For Parity you may enable tracing and disable pruning, in
|
||||
which case you can use some of the vendor specific JSON-RPC debug methods.
|
||||
|
||||
For many cases, you can probably rely on Etherscan, which dedicates large
|
||||
amounts of resources to tracking and storing this information.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Transaction History*
|
||||
|
||||
let etherscanProvider = new ethers.providers.EtherscanProvider();
|
||||
|
||||
etherscanProvider.getHistory(address).then((history) => {
|
||||
history.forEach((tx) => {
|
||||
console.log(tx);
|
||||
})
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
@TODO: Example of signing and verifying a hash in ethers and in Solidity.
|
||||
|
||||
-----
|
||||
|
||||
|
||||
.. EOF
|
@ -1,108 +0,0 @@
|
||||
Contracts
|
||||
*********
|
||||
|
||||
Some quick examples for techniques with Contracts.
|
||||
|
||||
-----
|
||||
|
||||
Return a Value from a State-Changing Method
|
||||
===========================================
|
||||
|
||||
Since a state-changing (non-constant) function can take a non-trivial
|
||||
amount of time to mine, the response is a transaction, and cannot
|
||||
return values directly.
|
||||
|
||||
Using events, we can simulate a return value from a non-constant function.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Solidity*
|
||||
|
||||
contract Example {
|
||||
event Return(uint256);
|
||||
|
||||
uint256 _accum = 0;
|
||||
|
||||
function increment() returns (uint256 sum) {
|
||||
_accum++;
|
||||
Returns(_accum);
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *JavaScript*
|
||||
|
||||
const assert = require('assert')
|
||||
|
||||
const {
|
||||
Contract,
|
||||
Wallet,
|
||||
getDefaultProvider
|
||||
} = require('ethers')
|
||||
|
||||
const provider = getDefaultProvider('ropsten')
|
||||
|
||||
const wallet = new Wallet(privateKey, provider)
|
||||
|
||||
const abi = [
|
||||
"event Return(uint256)",
|
||||
"function increment() returns (uint256 sum)"
|
||||
]
|
||||
|
||||
const contractAddress = "0x..."
|
||||
|
||||
const contract = new Contract(contractAddress, abi)
|
||||
|
||||
async function increment() {
|
||||
|
||||
// Call the contract, getting back the transaction
|
||||
let tx = await contract.increment()
|
||||
|
||||
// Wait for the transaction to have 2 confirmations.
|
||||
// See the note below on "Economic Value" for considerations
|
||||
// regarding the number of suggested confirmations
|
||||
let receipt = await tx.wait(2)
|
||||
|
||||
// The receipt will have an "events" Array, which will have
|
||||
// the emitted event from the Contract. The "Return(uint256)"
|
||||
// call is the last event.
|
||||
let sumEvent = receipt.events.pop()
|
||||
|
||||
// Not necessary; these are just for the purpose of this
|
||||
// example
|
||||
assert.equal(sumEvent.event, 'Return')
|
||||
assert.equal(sumEvent.eventSignature, 'Return(uint256)')
|
||||
|
||||
// The sum is the first (and in this case only) parameter
|
||||
// in the "Return(uint256 sum)" event
|
||||
let sum = sumEvent.args[0]
|
||||
|
||||
return sum
|
||||
}
|
||||
|
||||
increment.then((value) => {
|
||||
console.log(value);
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
Economic Incentives and Economic Value
|
||||
======================================
|
||||
|
||||
A very important aspect of Smart Contracts is consideration of the
|
||||
Economic Value being protected; even a completely logically correct
|
||||
Smart Contract can fall victim to misaligned economic incentives.
|
||||
|
||||
Imagine a city with an average parking ticket cost of $60, but the
|
||||
parking enforcement is not effective, allowing a person to park anytime
|
||||
and anywhere with only an average of 3 tickets per year. If
|
||||
the average cost to pay for parking is $10, and you park 100 times per year,
|
||||
the $180 in fines compared to the $1,000 in paying for parking is actually
|
||||
incentivizing illegal parking and disincentivizing paying legitimate parking.
|
||||
|
||||
This is a fairly involved topic, which we will write an article for
|
||||
and then link to from here, along with a related topic, "Miner
|
||||
Front-Running".
|
||||
|
||||
-----
|
||||
|
||||
.. eof
|
@ -1,122 +0,0 @@
|
||||
Providers
|
||||
*********
|
||||
|
||||
The Ethereum eco-system provides many methods of interacting with the
|
||||
blockchain. In ethers.js we expose a Provider API that covers the
|
||||
breadth of operations, however often it is useful to inter-operate with
|
||||
other existing APIs and libraries.
|
||||
|
||||
-----
|
||||
|
||||
MetaMask
|
||||
========
|
||||
|
||||
The MetaMask plug-in enables Ethereum for the Chrome browser, making it
|
||||
easy for people new ecosystem to get started, exposing the Ethereum
|
||||
network as a standard Web3 Provider.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Connecting to MetaMask*
|
||||
|
||||
// MetaMask injects a Web3 Provider as "web3.currentProvider", so
|
||||
// we can wrap it up in the ethers.js Web3Provider, which wraps a
|
||||
// Web3 Provider and exposes the ethers.js Provider API.
|
||||
|
||||
const provider = new ethers.providers.Web3Provider(web3.currentProvider);
|
||||
|
||||
// There is only ever up to one account in MetaMask exposed
|
||||
const signer = provider.getSigner();
|
||||
|
||||
-----
|
||||
|
||||
TestRPC / Ganache
|
||||
=================
|
||||
|
||||
The Ganache (formerly TestRPC) is a mock-blockchain which helps create
|
||||
temporary instances of an Ethereum node for testing.
|
||||
|
||||
**NOTE:**
|
||||
Ganache is not entirely standards-compliant and may
|
||||
not always behave properly. If you are getting deeper
|
||||
into Ethereum development, we recommend installing
|
||||
Geth or Parity and using a development chain
|
||||
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Connecting to a Ganache Node*
|
||||
|
||||
// Once a Ganache node is running, it behaves very similar to a
|
||||
// JSON-RPC API node.
|
||||
|
||||
const url = "http://localhost:8545";
|
||||
|
||||
// Or if you are running the UI version, use this instead:
|
||||
// const url = "http://localhost:7545"
|
||||
|
||||
const provider = new ethers.providers.JsonRpcProvider(url);
|
||||
|
||||
// Getting the accounts
|
||||
const signer0 = provider.getSigner(0);
|
||||
const signer1 = provider.getSigner(1);
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Using an In-Process Ganache Instance*
|
||||
|
||||
// If you would like to run an in-process instance of Ganache, you can
|
||||
// use a method similar to the MetaMask solution; we wrap the Web3 Provider
|
||||
// API with an ethers.js Web3Provider, which exposes the ethers.js API
|
||||
// from a Web3 Provider
|
||||
|
||||
const ganache = Ganache.provider(ganacheOptions);
|
||||
const provider = new ethers.providers.Web3Provider(ganache);
|
||||
|
||||
// Getting the accounts
|
||||
const signer0 = provider.getSigner(0);
|
||||
const signer1 = provider.getSigner(1);
|
||||
|
||||
-----
|
||||
|
||||
Custom Provider
|
||||
===============
|
||||
|
||||
This is a much more advanced topic, and most people should not need to work this
|
||||
low level. But it is provided for those rare instances where you need some custom
|
||||
connection logic.
|
||||
|
||||
A provider must only implement the method **perform(method, params)**. All data passed
|
||||
into a provider is sanitized by the BaseProvider subclass, and all results are normalized
|
||||
before returning them to the user.
|
||||
|
||||
For this example, we will build a DebugProvider, which will simple proxy all commands
|
||||
through another Provider, but dump all data going back and forth.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Sub-Class Provider for Debugging*
|
||||
|
||||
const ethers = require('ethers');
|
||||
|
||||
class DebugProvider extends ethers.providers.BaseProvider {
|
||||
readonly provider: ethers.providers.Provider;
|
||||
|
||||
constructor(provider) {
|
||||
super(provider.getNetork());
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
// This should return a Promise (and may throw erros)
|
||||
// method is the method name (e.g. getBalance) and params is an
|
||||
// object with normalized values passed in, depending on the method
|
||||
perform(method, params): Promise<any> {
|
||||
this.provider.perform(method, params).then((result) => {
|
||||
console.log('DEBUG', method, params, '=>', result);
|
||||
}, (error) => {
|
||||
console.log('DEBUG:ERROR', method, params, '=>', error);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
-----
|
||||
|
||||
.. EOF
|
@ -1,93 +0,0 @@
|
||||
React Native
|
||||
************
|
||||
|
||||
The React Native environment has a lot of little quirks, so this
|
||||
documentation is aimed at helping smooth those over.
|
||||
|
||||
Please feel free to create issues on GitHub for recommendations and
|
||||
additions to this document.
|
||||
|
||||
-----
|
||||
|
||||
Shims
|
||||
=====
|
||||
|
||||
There are several pieces of functionality missing from the current version
|
||||
of React-Native. Here is a list of functionality the ``ethers/dist/shims`` will
|
||||
provide if unavailable:
|
||||
|
||||
- ``ArrayBuffer.isView``
|
||||
- ``atob`` and ``btoa``
|
||||
- ``nextTick``
|
||||
- ``Promise``
|
||||
- ``String.prototype.normalize``
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Importing into React-Native*
|
||||
|
||||
// Import the required shims
|
||||
import 'ethers/dist/shims.js';
|
||||
|
||||
// Import the ethers library
|
||||
import { ethers } from 'ethers';
|
||||
|
||||
**Note**
|
||||
This file **must** be minified, since the React-Native ``require``
|
||||
seems to hijack the variable name ``require``, even when encapsulated
|
||||
inside a closure. The minification process replaces the ``require``
|
||||
with a mangled name, so that there is no collision. This is hacky
|
||||
and in the future a better method would be preferred. Suggestions?
|
||||
|
||||
**Note**
|
||||
As we find additional features not generally available, they will
|
||||
be added to this shim file. It is targetted toward platforms where
|
||||
build-size is not critical, so functionality is favored over keeping
|
||||
the size small.
|
||||
|
||||
-----
|
||||
|
||||
Wordlists
|
||||
=========
|
||||
|
||||
React-Native will pull in the browser version of ethers, which does not have
|
||||
all the additional word lists, by default. Each desired wordlist must be
|
||||
separately imported.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Importing languages into React-Native*
|
||||
|
||||
import {
|
||||
es, // Spanish
|
||||
fr, // French
|
||||
it, // Italian
|
||||
ja, // Japanese
|
||||
ko, // Korean
|
||||
zh_ch, // Chinese (simplified)
|
||||
zh_tw // Chinese (tranditional)
|
||||
} from 'ethers/wordlists';
|
||||
|
||||
-----
|
||||
|
||||
Other Notes
|
||||
===========
|
||||
|
||||
**console.log**
|
||||
The use of ``console.log`` in React Native `can substantially impact performance`_.
|
||||
For this reason, you may wish to reduce the log level to not show info and warnings.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Change Log Level*
|
||||
|
||||
// The default is "info"; other options
|
||||
// "debug", "info", "warn", "error", "off"
|
||||
ethers.errors.setLogLevel("error");
|
||||
|
||||
-----
|
||||
|
||||
TODO: Include instructions on installing crypto performance
|
||||
|
||||
- scrypt (it is VERY slow in React Native; too slow to be functional)
|
||||
|
||||
.. _can substantially impact performance: https://docs.expo.io/versions/latest/react-native/performance/#using-consolelog-statements
|
||||
|
||||
.. EOF
|
@ -1,209 +0,0 @@
|
||||
Signing Messages
|
||||
****************
|
||||
|
||||
Signing messages can be used for various method of authentication and off-chain
|
||||
operations, which can be put on-chain if necessary.
|
||||
|
||||
-----
|
||||
|
||||
Signing a String Message
|
||||
========================
|
||||
|
||||
By allowing a user to sign a string, which can be verified on-chain, interesting
|
||||
forms of authentication on-chain can be achieved. This is a quick example of how
|
||||
an arbitrary string can be signed by a private key, and verified on-chain. The
|
||||
Contract can be called by another Contract, for example, before unlocking
|
||||
functionality by the caller.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Solidity Contract*
|
||||
|
||||
contract Verifier {
|
||||
// Returns the address that signed a given string message
|
||||
function verifyString(string message, uint8 v, bytes32 r,
|
||||
bytes32 s) public pure returns (address signer) {
|
||||
|
||||
// The message header; we will fill in the length next
|
||||
string memory header = "\x19Ethereum Signed Message:\n000000";
|
||||
|
||||
uint256 lengthOffset;
|
||||
uint256 length;
|
||||
assembly {
|
||||
// The first word of a string is its length
|
||||
length := mload(message)
|
||||
// The beginning of the base-10 message length in the prefix
|
||||
lengthOffset := add(header, 57)
|
||||
}
|
||||
|
||||
// Maximum length we support
|
||||
require(length <= 999999);
|
||||
|
||||
// The length of the message's length in base-10
|
||||
uint256 lengthLength = 0;
|
||||
|
||||
// The divisor to get the next left-most message length digit
|
||||
uint256 divisor = 100000;
|
||||
|
||||
// Move one digit of the message length to the right at a time
|
||||
while (divisor != 0) {
|
||||
|
||||
// The place value at the divisor
|
||||
uint256 digit = length / divisor;
|
||||
if (digit == 0) {
|
||||
// Skip leading zeros
|
||||
if (lengthLength == 0) {
|
||||
divisor /= 10;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Found a non-zero digit or non-leading zero digit
|
||||
lengthLength++;
|
||||
|
||||
// Remove this digit from the message length's current value
|
||||
length -= digit * divisor;
|
||||
|
||||
// Shift our base-10 divisor over
|
||||
divisor /= 10;
|
||||
|
||||
// Convert the digit to its ASCII representation (man ascii)
|
||||
digit += 0x30;
|
||||
// Move to the next character and write the digit
|
||||
lengthOffset++;
|
||||
|
||||
assembly {
|
||||
mstore8(lengthOffset, digit)
|
||||
}
|
||||
}
|
||||
|
||||
// The null string requires exactly 1 zero (unskip 1 leading 0)
|
||||
if (lengthLength == 0) {
|
||||
lengthLength = 1 + 0x19 + 1;
|
||||
} else {
|
||||
lengthLength += 1 + 0x19;
|
||||
}
|
||||
|
||||
// Truncate the tailing zeros from the header
|
||||
assembly {
|
||||
mstore(header, lengthLength)
|
||||
}
|
||||
|
||||
// Perform the elliptic curve recover operation
|
||||
bytes32 check = keccak256(header, message);
|
||||
|
||||
return ecrecover(check, v, r, s);
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *JavaScript*
|
||||
|
||||
let abi = [
|
||||
"function verifyString(string, uint8, bytes32, bytes32) public pure returns (address)"
|
||||
];
|
||||
|
||||
let provider = ethers.getDefaultProvider('ropsten');
|
||||
|
||||
// Create a wallet to sign the message with
|
||||
let privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
|
||||
let wallet = new ethers.Wallet(privateKey);
|
||||
|
||||
console.log(wallet.address);
|
||||
// "0x14791697260E4c9A71f18484C9f997B308e59325"
|
||||
|
||||
let contractAddress = '0x80F85dA065115F576F1fbe5E14285dA51ea39260';
|
||||
let contract = new Contract(contractAddress, abi, provider);
|
||||
|
||||
let message = "Hello World";
|
||||
|
||||
// Sign the string message
|
||||
let flatSig = await wallet.signMessage(message);
|
||||
|
||||
// For Solidity, we need the expanded-format of a signature
|
||||
let sig = ethers.utils.splitSignature(flatSig);
|
||||
|
||||
// Call the verifyString function
|
||||
let recovered = await contract.verifyString(message, sig.v, sig.r, sig.s);
|
||||
|
||||
console.log(recovered);
|
||||
// "0x14791697260E4c9A71f18484C9f997B308e59325"
|
||||
|
||||
-----
|
||||
|
||||
Signing a Digest Hash
|
||||
=====================
|
||||
|
||||
Signing a digest can be far more space efficient than signing an arbitrary
|
||||
string (as you probably notice when comparing the length of the Solidity
|
||||
source code), however, with this method, many Wallet UI would not be able to
|
||||
fully inform the user what they are about to sign, so this method should only
|
||||
be used in quite specific cases, such as in custom Wallet applications.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Solidity Contract*
|
||||
|
||||
contract Verifier {
|
||||
function verifyHash(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public pure
|
||||
returns (address signer) {
|
||||
|
||||
bytes32 messageDigest = keccak256("\x19Ethereum Signed Message:\n32", hash);
|
||||
|
||||
return ecrecover(messageDigest, v, r, s);
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *JavaScript*
|
||||
|
||||
let abi = [
|
||||
"function verifyHash(bytes32, uint8, bytes32, bytes32) public pure returns (address)"
|
||||
];
|
||||
|
||||
let provider = ethers.getDefaultProvider('ropsten');
|
||||
|
||||
// Create a wallet to sign the hash with
|
||||
let privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
|
||||
let wallet = new ethers.Wallet(privateKey);
|
||||
|
||||
console.log(wallet.address);
|
||||
// "0x14791697260E4c9A71f18484C9f997B308e59325"
|
||||
|
||||
let contractAddress = '0x80F85dA065115F576F1fbe5E14285dA51ea39260';
|
||||
let contract = new ethers.Contract(contractAddress, abi, provider);
|
||||
|
||||
// The hash we wish to sign and verify
|
||||
let messageHash = ethers.utils.id("Hello World");
|
||||
|
||||
// Note: messageHash is a string, that is 66-bytes long, to sign the
|
||||
// binary value, we must convert it to the 32 byte Array that
|
||||
// the string represents
|
||||
//
|
||||
// i.e.
|
||||
// // 66-byte string
|
||||
// "0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba"
|
||||
//
|
||||
// ... vs ...
|
||||
//
|
||||
// // 32 entry Uint8Array
|
||||
// [ 89, 47, 167, 67, 136, 159, 199, 249, 42, 194, 163,
|
||||
// 123, 177, 245, 186, 29, 175, 42, 92, 132, 116, 28,
|
||||
// 160, 224, 6, 29, 36, 58, 46, 103, 7, 186]
|
||||
|
||||
let messageHashBytes = ethers.utils.arrayify(messageHash)
|
||||
|
||||
// Sign the binary data
|
||||
let flatSig = await wallet.signMessage(messageHashBytes);
|
||||
|
||||
// For Solidity, we need the expanded-format of a signature
|
||||
let sig = ethers.utils.splitSignature(flatSig);
|
||||
|
||||
// Call the verifyHash function
|
||||
let recovered = await contract.verifyHash(messageHash, sig.v, sig.r, sig.s);
|
||||
|
||||
console.log(recovered);
|
||||
// "0x14791697260E4c9A71f18484C9f997B308e59325"
|
||||
|
||||
-----
|
||||
|
||||
.. EOF
|
||||
|
@ -1,77 +0,0 @@
|
||||
Testing
|
||||
*******
|
||||
|
||||
Some quick examples for techniques for testing.
|
||||
|
||||
-----
|
||||
|
||||
Contract Events
|
||||
===============
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Testing ERC-20 Transfer Event*
|
||||
|
||||
describe('Events', async function() {
|
||||
it('triggers a Transfer event', function() {
|
||||
|
||||
let contract = new Contract(contractAddress, abi, accounts[0]);
|
||||
|
||||
let transferEvent = new Promise((resolve, reject) => {
|
||||
contract.on('Transfer', (from, to, amount, event) => {
|
||||
event.removeListener();
|
||||
|
||||
resolve({
|
||||
from: from,
|
||||
to: to,
|
||||
amount: amount
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
reject(new Error('timeout'));
|
||||
}, 60000)
|
||||
});
|
||||
|
||||
let tx = await contract.transfer(accounts[1], 12345);
|
||||
|
||||
let event = await transferEvent;
|
||||
|
||||
assert.equal(event.from, account[0].address);
|
||||
assert.equal(event.to, account[1].address);
|
||||
assert.equal(event.amount.toNumber(), 12345);
|
||||
});
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
Using Multiple Accounts
|
||||
=======================
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Testing Multiple Accounts*
|
||||
|
||||
describe('Events', async function() {
|
||||
it('triggers a Transfer event', function() {
|
||||
|
||||
// Connect to Geth/Parity node on http://localhost:8545
|
||||
let provider = new providers.JsonRpcProvider();
|
||||
|
||||
// Get the first two accounts from the Geth/Parity node
|
||||
let signer0 = provider.getSigner(0);
|
||||
let signer1 = provider.getSigner(1);
|
||||
|
||||
// Read-only connection to the contract
|
||||
let contract = new Contract(contractAddress, abi, provider);
|
||||
|
||||
// Read-Write connection to the contract
|
||||
let contractAsSigner0 = contract.connect(signer0);
|
||||
let contractAsSigner1 = contract.connect(signer1);
|
||||
|
||||
// ...
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
.. eof
|
@ -1,27 +0,0 @@
|
||||
|
||||
Cookbook
|
||||
********
|
||||
|
||||
This is a small (but growing) collection of simple recipes to perform common tasks
|
||||
with the Ethereum blockchain and Ethereum accounts.
|
||||
|
||||
Some of these recipes are stubs that will be filled in shortly.
|
||||
|
||||
If there is a simple recipe you would like added, please send suggestions to support@ethers.io.
|
||||
|
||||
-----
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
cookbook-accounts
|
||||
cookbook-contracts
|
||||
cookbook-providers
|
||||
cookbook-signing
|
||||
cookbook-testing
|
||||
cookbook-react
|
||||
|
||||
-----
|
||||
|
||||
.. EOF
|
||||
|
@ -1,56 +0,0 @@
|
||||
Getting Started
|
||||
***************
|
||||
|
||||
The ethers.js library is a compact and complete JavaScript library for Ethereum.
|
||||
|
||||
-----
|
||||
|
||||
Installing in Node.js
|
||||
=====================
|
||||
|
||||
Install the ethers.js library from your project directory::
|
||||
|
||||
/home/ricmoo/my-project> npm install --save ethers
|
||||
|
||||
Importing
|
||||
---------
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *JavaScript (ES3)*
|
||||
|
||||
var ethers = require('ethers');
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *JavaScript (ES5 or ES6)*
|
||||
|
||||
const ethers = require('ethers');
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *JavaScript (ES6) / TypeScript*
|
||||
|
||||
import { ethers } from 'ethers';
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Including in Web Applications
|
||||
=============================
|
||||
|
||||
For security purposes, it is usually best to place a **copy** of `this script`_ on
|
||||
the application's server, but for a quick prototype using the Ethers CDN (content
|
||||
distribution network) should suffice.
|
||||
|
||||
.. code-block:: html
|
||||
:caption: *HTML*
|
||||
|
||||
<!-- This exposes the library as a global variable: ethers -->
|
||||
<script src="https://cdn.ethers.io/scripts/ethers-v4.min.js"
|
||||
charset="utf-8"
|
||||
type="text/javascript">
|
||||
</script>
|
||||
|
||||
|
||||
-----
|
||||
|
||||
.. _npm is installed: https://nodejs.org/en/
|
||||
.. _this script: https://cdn.ethers.io/scripts/ethers-v4.min.js
|
@ -1,66 +0,0 @@
|
||||
.. ethers.js documentation master file, created by
|
||||
sphinx-quickstart on Tue Nov 29 10:25:33 2016.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
|
||||
What is ethers.js
|
||||
*****************
|
||||
|
||||
The ethers.js library aims to be a complete and compact library for interacting
|
||||
with the Ethereum Blockchain and its ecosystem. It was originally designed for
|
||||
use with `ethers.io`_ and has since expanded into a much more general-purpose
|
||||
library.
|
||||
|
||||
-----
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
- Keep your private keys in your client, **safe** and sound
|
||||
- Import and export **JSON wallets** (Geth, Parity and crowdsale)
|
||||
- Import and export BIP 39 **mnemonic phrases** (12 word backup phrases) and HD Wallets (English, Italian, Japanese, Korean, Simplified Chinese, Traditional Chinese; more coming soon)
|
||||
- Meta-classes create JavaScript objects from any contract ABI, including **ABIv2** and **Human-Readable** ABI
|
||||
- Connect to Ethereum nodes over `JSON-RPC`_, `INFURA`_, `Etherscan`_, or `MetaMask`_.
|
||||
- **ENS names** are first-class citizens; they can be used anywhere an Ethereum addresses can be used
|
||||
- **Tiny** (~88kb compressed; 284kb uncompressed)
|
||||
- **Complete** functionality for all your Ethereum needs
|
||||
- Extensive `documentation`_
|
||||
- Large collection of **test cases** which are maintained and added to
|
||||
- Fully **TypeScript** ready, with definition files and full TypeScript source
|
||||
- **MIT License** (including ALL dependencies); completely open source to do with as you please
|
||||
|
||||
----
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
:caption: Developer Documentation
|
||||
|
||||
getting-started
|
||||
api
|
||||
api-advanced
|
||||
cookbook
|
||||
migration
|
||||
notes
|
||||
testing
|
||||
|
||||
-----
|
||||
|
||||
Legacy Documentation
|
||||
====================
|
||||
|
||||
For documentation of older versions, we will keep a list here.
|
||||
|
||||
- `version 3.0`_
|
||||
|
||||
-----
|
||||
|
||||
.. _ethers.io: https://ethers.io
|
||||
.. _JSON-RPC: https://github.com/ethereum/wiki/wiki/JSON-RPC
|
||||
.. _INFURA: https://infura.io/
|
||||
.. _Etherscan: https://etherscan.io/
|
||||
.. _MetaMask: https://metamask.io/
|
||||
.. _version 3.0: https://docs.ethers.io/ethers.js/v3.0/html/
|
||||
.. _documentation: https://docs.ethers.io
|
||||
|
||||
.. EOF
|
@ -1,654 +0,0 @@
|
||||
.. _migration:
|
||||
|
||||
Migration Guides
|
||||
****************
|
||||
|
||||
Migrating from Web3 to ethers v4
|
||||
================================
|
||||
|
||||
Todo: This is coming soon.
|
||||
|
||||
-----
|
||||
|
||||
Migrating from ethers v3 to ethers v4
|
||||
=====================================
|
||||
|
||||
A lot of the functionality has remained the same, but there has been some
|
||||
slight refactoring and improved paradigms.
|
||||
|
||||
-----
|
||||
|
||||
Constants
|
||||
---------
|
||||
|
||||
All constants have moved from the utils to the root ethers object.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Constants --- ethers v3*
|
||||
|
||||
let one = ethers.utils.constants.One;
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Constants --- ethers v4*
|
||||
|
||||
let one = ethers.constants.One;
|
||||
|
||||
-----
|
||||
|
||||
Deploying Contracts
|
||||
-------------------
|
||||
|
||||
Deploying contracts has undergone some significant change. The new API is more
|
||||
seamless and reduces the amount of low-level understanding that is required,
|
||||
such as the details of how *init transaction* behave.
|
||||
|
||||
More complex and complete objects are also returned through-out the
|
||||
process, so there are far less calls to utility functions after a deployment
|
||||
required to populate databases and track status.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *ethers v3*
|
||||
|
||||
let tx = Contract.getDeployTransaction(bytecode, abi, arg1, arg2);
|
||||
|
||||
// overrides
|
||||
tx.gasLimit = 1500000;
|
||||
|
||||
wallet.sendTransaction(tx).then((tx) => {
|
||||
let contractAddress = ethers.utils.getContractAddress(tx);
|
||||
let contract = new ethers.Contract(contractAddress, abi, wallet);
|
||||
provider.waitForTransaction(tx).then((tx) => {
|
||||
provider.getTransactionReceipt(tx.hash).then((receipt) => {
|
||||
if (receipt.status === 0) {
|
||||
throw new Error('failed to deploy');
|
||||
}
|
||||
contract.someFunction();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Deploy a Contract --- ethers v4*
|
||||
|
||||
// Construct a Contract Factory
|
||||
let factory = new ethers.ContractFactory(abi, bytecode, signer);
|
||||
|
||||
// Deploy automatically detects gasLimit and all other parameters
|
||||
// Overrides can optionally be passed as an extra parameter
|
||||
|
||||
// Optional; all unspecified values will queried from the network
|
||||
let overrides = { };
|
||||
|
||||
factory.deploy(arg1, arg2, overrides).then((contract) => {
|
||||
// The contract is returned immediately; it has not been mined yet
|
||||
|
||||
// The contract known its address (before it is even mined)
|
||||
console.log(contract.address);
|
||||
|
||||
// You can access the in-flight transaction that is currently waiting to be mined
|
||||
console.log(contract.deployTransaction);
|
||||
// A full transaction with:
|
||||
// - from
|
||||
// - nonce
|
||||
// - hash
|
||||
// - all other Transaction Response fields
|
||||
// - wait() => Promise that resolves to the TransactionReceipt
|
||||
|
||||
// The "deployed()" function will return a Promise that resolves
|
||||
// to the contract, once it has been mined. If the contract fails
|
||||
// to deploy, the Promise will reject.
|
||||
contract.deployed().then((contract) => {
|
||||
// The contract is now deployed
|
||||
contract.someFunction();
|
||||
|
||||
}, (error) => {
|
||||
// The transaction failed to deploy
|
||||
console.log('Failed to deploy in TX:', error.transactionHash);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Deploy a Contract with async/await --- ethers v4*
|
||||
|
||||
async function() {
|
||||
let factory = new ethers.ContractFactory(abi, bytecode, signer);
|
||||
|
||||
let contract = await factory.deploy(arg1, arg2);
|
||||
|
||||
try {
|
||||
await contract.deployed();
|
||||
|
||||
} catch (error) {
|
||||
console.log('Failed to deploy in TX:', error.transactionHash);
|
||||
throw error;
|
||||
}
|
||||
|
||||
contract.someFunction();
|
||||
}
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Get Deployment Transaction --- ethers v4*
|
||||
|
||||
// If you still need the deployment transaction, and don't wish to
|
||||
// actually deploy, you can much more easily just use the Interface
|
||||
// object without the need for a provider or signer.
|
||||
|
||||
let factory = new ethers.ContractFactory(abi, bytecode);
|
||||
|
||||
let tx = factory.getDeployTransaction(arg1, arg2);
|
||||
|
||||
-----
|
||||
|
||||
Encrypted Wallets
|
||||
-----------------
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Checking JSON Wallets --- ethers v3*
|
||||
|
||||
let isJsonWallet = ethers.Wallet.isEncryptedWallet(json);
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Checking JSON Wallets --- ethers v4*
|
||||
|
||||
let address = ethers.utils.getJsonWalletAddress(json);
|
||||
let isJsonWallet = (address !== null)
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Decrypting JSON Wallets --- ethers v3*
|
||||
|
||||
let wallet = await ethers.Wallet.fromEncryptedWallet(json, password);
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Decrypting JSON Wallets --- ethers v4*
|
||||
|
||||
let wallet = await ethers.Wallet.fromEncryptedJson(json, password);
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
Events now behave like a modern JavaScript Event Emitter, rather than a 1995
|
||||
web browser.
|
||||
|
||||
The events now play nicer with the arrow operator (i.e. ``() => { ... }``),
|
||||
since rather than modifying the `this` inside the callbacks, an additional
|
||||
rich object is passed along.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Events --- ethers v3*
|
||||
|
||||
// Solidity: event SomeEvent(address foo, uint256 bar)
|
||||
contract.onsomeevent = function(foo, bar) {
|
||||
console.log(foo, bar);
|
||||
// The Log was available at this:
|
||||
// - this.event
|
||||
// - this.removeListener()
|
||||
};
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Listening to an Event --- ethers v4*
|
||||
|
||||
// Solidity: event SomeEvent(address foo, uint256 bar)
|
||||
contract.on('SomeEvent', (foo, bar, eventInfo) => {
|
||||
console.log(foo, bar);
|
||||
// eventInfo
|
||||
// - Log (blockNumber, blockHash, txHash, removed, address, data, etc.)
|
||||
// - args: [ foo, bar ]
|
||||
// - decode: (data, topics) => [ foo, bar ]
|
||||
// - event: "SomeEvent"
|
||||
// - eventSignature: "SomeEvent(address,uint256)"
|
||||
// - removeListener: () => removes this listener
|
||||
// - getBlock: () => returns a Promise of the block
|
||||
// - getTransaction: () => returns a Promise of transaction
|
||||
// - getTransactionReceipt: () => returns a Promise of the receipt
|
||||
});
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Indexed Events --- ethers v3*
|
||||
|
||||
// Detecting a parameters is an indexed hash, and not a value
|
||||
contract.someEvent = function(foo) {
|
||||
if (foo.indexed) {
|
||||
// The value is just a hash to filter by
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Indexed Events --- ethers v4*
|
||||
|
||||
let Indexed = ethers.types.Indexed;
|
||||
|
||||
// Detecting a parameters is an indexed hash, and not a value
|
||||
contract.someEvent = function(foo) {
|
||||
if (Indexed.isIndexed(foo)) {
|
||||
// The value is just a hash to filter by
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Filtering Events --- ethers v4*
|
||||
|
||||
// Solidity: event SomeEvent(address indexed foo, uint256 bar)
|
||||
let address = '0x8B40a2E27C5E87aa66DfA7F80BF675176F49DCA7';
|
||||
let filter = contract.filters.SomeEvent(address, null);
|
||||
|
||||
console.log(filter);
|
||||
// {
|
||||
// address: contract.address,
|
||||
// topics: [
|
||||
// 0xdde371250dcd21c331edbb965b9163f4898566e8c60e28868533281edf66ab03,
|
||||
// 0x0000000000000000000000008b40a2e27c5e87aa66dfa7f80bf675176f49dca7
|
||||
// ]
|
||||
// }
|
||||
|
||||
contract.on(filter, (foo, bar, eventInfo) => {
|
||||
console.log(foo === address);
|
||||
// true
|
||||
});
|
||||
|
||||
|
||||
If there are multiple events with the same name:
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Event Name Collision --- ethers v4*
|
||||
|
||||
// Solidity
|
||||
// - event SomeEvent(address foo, uint256 bar)
|
||||
// - event SomeEvent(address foo, address bar)
|
||||
|
||||
contract.on('SomeEvent(address, uint256)', (foo, bar, eventInfo) => {
|
||||
// ...
|
||||
});
|
||||
|
||||
contract.on('SomeEvent(address, address)', (foo, bar, eventInfo) => {
|
||||
// ...
|
||||
});
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Fetching JSON
|
||||
-------------
|
||||
|
||||
The JSON fetching routine, since it was mostly used for Providers was
|
||||
on the Provider object in v3. In v4, it has moved to utils, since there
|
||||
are other common cases where it may be useful.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Fetching JSON --- ethers v3*
|
||||
|
||||
ethers.providers.Provider.fetchJson(url).then((object) => {
|
||||
// ...
|
||||
});
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Fetching JSON --- ethers v4*
|
||||
|
||||
ethers.utils.fetchJson(url).then((object) => {
|
||||
// ...
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
Interfaces
|
||||
----------
|
||||
|
||||
This has always been a fairly low-level API, and mostly available for
|
||||
framework developers and other tools that require quite specific access
|
||||
to working with an ABI. Most of the changes are to simplify the interaction,
|
||||
so while there will probably be changes required, if you use this class, the
|
||||
complexity and size of your code should be reduced.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Interface --- ethers v3*
|
||||
|
||||
let iface = ethers.Interface(address, abi, providerOrSigner);
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Interface --- ethers v4*
|
||||
|
||||
let iface = ethers.utils.Interface(address, abi, providerOrSigner);
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Function Description --- ethers v3*
|
||||
|
||||
let address = "0x8B40a2E27C5E87aa66DfA7F80BF675176F49DCA7";
|
||||
let value = 1000;
|
||||
|
||||
// Solidity: function someFunc(address foo, uint256 bar) constant returns (address result)
|
||||
let functionCallable = iface.functionst.someFunc
|
||||
// functionInfo
|
||||
// - inputs: { names: [ "foo", "bar" ], types: [ "address", "uint256" ] }
|
||||
// - outputs: { names: [ "result" ], types: [ "address" ] }
|
||||
// - payable: false
|
||||
// - parseResult: (data) => any
|
||||
// - signature: "someFunc(address,uint256)"
|
||||
// - sighash: "0xd90a6a67"
|
||||
|
||||
let data = functionCallable(address, value);
|
||||
let result = functionCallable.parseResult(callResultData);
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Function Description --- ethers v4*
|
||||
|
||||
let address = "0x8B40a2E27C5E87aa66DfA7F80BF675176F49DCA7";
|
||||
let value = 1000;
|
||||
|
||||
// Solidity: function someFunc(address foo, uint256 bar) constant returns (address result)
|
||||
let functionInfo = iface.functions.someFunc;
|
||||
// functionInfo
|
||||
// - type: "call" (or "transaction" for non-constant functions)
|
||||
// - name: "someFunc"
|
||||
// - signature: "someFunc(address,uint256)"
|
||||
// - sighash: "0xd90a6a67"
|
||||
// - inputs: [ { name: foo", type: "bar" }, { name: "bar", type: "uint256" } ]
|
||||
// - outputs: [ { name: "result", type: "address" } ]
|
||||
// - payable: false
|
||||
// - encode: (params) => string
|
||||
// - decode: (data) => any
|
||||
|
||||
// Note: This takes in an array; it no longer uses ...args
|
||||
let data = functionInfo.encode([ address, value ]);
|
||||
let result = functionInfo.decode(callResultData);
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Event Description --- ethers v3*
|
||||
|
||||
// Solidity: event SomeEvent(address foo, uint256 bar)
|
||||
let eventInfo = iface.events.SomeEvent;
|
||||
// eventInfo
|
||||
// - topics: [ ethers.utils.id("SomeEvent(address,uint256)") ]
|
||||
// - anonymous: false
|
||||
// - name: "SomeEvent"
|
||||
// - signature: "SomeEvent(address,uint256)"
|
||||
// - type: "event"
|
||||
// - inputs: { names: [ 'foo', 'bar' ], types: [ 'address', 'uint256' ] }
|
||||
// - parse: (topics, data) => Result
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Event Description --- ethers v4*
|
||||
|
||||
// Solidity: event SomeEvent(address foo, uint256 bar)
|
||||
let eventInfo = iface.events.SomeEvent;
|
||||
// eventInfo
|
||||
// - name: "SomeEvent"
|
||||
// - signature: "SomeEvent(address,bar)"
|
||||
// - inputs: [ { name: "foo", type: "address" }, { name: "bar", type: "uint256" } ]
|
||||
// - anonymous: false
|
||||
// - topic: ethers.utils.id("SomeEvent(address,uint256)")
|
||||
// - encodeTopics: (Array<any>) => Array<string>
|
||||
// - decode: (data, topics) => Result
|
||||
|
||||
// Create event filter topics
|
||||
let address = '0x8B40a2E27C5E87aa66DfA7F80BF675176F49DCA7';
|
||||
eventInfo.encodeTopics(address, null)
|
||||
// topics: [
|
||||
// "0xdde371250dcd21c331edbb965b9163f4898566e8c60e28868533281edf66ab03",
|
||||
// "0x0000000000000000000000008b40a2e27c5e87aa66dfa7f80bf675176f49dca7"
|
||||
// ]
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Networks
|
||||
--------
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Getting Network Info - ethers v3*
|
||||
|
||||
let network = ethers.providers.getNetwork('ropsten')
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Getting Network Info - ethers v4*
|
||||
|
||||
let network = ethers.utils.getNetwork('ropsten');
|
||||
|
||||
// Networks may now also be found by their network ID
|
||||
let network = ethers.utils.getNetwork(3);
|
||||
|
||||
// And networks can be validated as an object
|
||||
let network = ethers.utils.getNetwork({ name: "custom", chainId: 1337 });
|
||||
|
||||
// Validation fails; this will throw an error, since Rinkeby is not
|
||||
// chain ID 1 (homestead is)
|
||||
let network = ethers.utils.getNetwork({ name: "rinkeby", chainId: 1 });
|
||||
|
||||
-----
|
||||
|
||||
Parsing Transactions
|
||||
--------------------
|
||||
|
||||
The transaction parsing was moved out of the Wallet and into its own class
|
||||
in the utilities, along with a general serialization API.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *ethers v3*
|
||||
|
||||
let tx = ethers.Wallet.parseTransaction(rawTransaction);
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *ethers v4*
|
||||
|
||||
let tx = ethers.utils.parseTransaction(rawTransaction);
|
||||
|
||||
-----
|
||||
|
||||
Custom Signer
|
||||
-------------
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Custome Signer --- ethers v3*
|
||||
|
||||
let signer = {
|
||||
// Required
|
||||
getAddress: function() { ... },
|
||||
provider: provider,
|
||||
|
||||
// Optional
|
||||
estimateGas: function(tx) { ... },
|
||||
sendTransaction: function(tx) { ... },
|
||||
sign: function(tx) { ... },
|
||||
};
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Custom Signer --- JavaScript --- ethers v4*
|
||||
|
||||
function CustomSigner {
|
||||
ethers.Signer.call(this);
|
||||
|
||||
// Optional
|
||||
this.provider = provider;
|
||||
}
|
||||
inherits(CustomSigner, ethers.Signer);
|
||||
|
||||
// Required
|
||||
CustomSigner.prototype.getAddress = () => { ... };
|
||||
CustomSigner.prototype.sendTransaction = (tx) => { ... };
|
||||
CustomSigner.prototype.signMessage = (message) => { ... };
|
||||
|
||||
// Optional
|
||||
CustomSigner.prototype.connect = (provider) => { ... };
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Custom Signer --- TypeScript --- ethers v4*
|
||||
|
||||
import {
|
||||
Signer,
|
||||
utils
|
||||
} from 'ethers';
|
||||
|
||||
import {
|
||||
Provider,
|
||||
TransactionRequest,
|
||||
TransactionReqponse
|
||||
} from 'ethers/providers';
|
||||
|
||||
class CustomSigner extends Signer {
|
||||
this.provider = provider;
|
||||
readony provider: Provider;
|
||||
constructor(provider) {
|
||||
super();
|
||||
|
||||
// Optional
|
||||
this.provider = // ...
|
||||
}
|
||||
|
||||
getAddress() {
|
||||
// Return a promise to the address
|
||||
};
|
||||
|
||||
sendTransaction = (transaction: TransactionRequest): Promise<TransaxctinResponse> {
|
||||
// This will popualte missing properties, like nonce, gasLimit, etc
|
||||
return utils.populateTransaction(transaction).then((tx) => {
|
||||
// Send the transaction and resolve the transaction
|
||||
});
|
||||
};
|
||||
|
||||
signMessage(message: string | ethers.utils.Arrayish): Promise<string> {
|
||||
let dataToSign: Uint8Array = utils.hashMessage(message);;
|
||||
// Sign ths dataToSign and resolve teh flat-format signature
|
||||
};
|
||||
|
||||
// Optional; highly recommended
|
||||
connect(provider: Provider): CustomSigner {
|
||||
return new CustomSigner(provider);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-----
|
||||
|
||||
Default Provider
|
||||
----------------
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Default Provider --- ethers v3*
|
||||
|
||||
let provider = ethers.providers.getDefaultProvider();
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Default Provider --- ethers v4*
|
||||
|
||||
let provider = ethers.getDefaultProvider();
|
||||
|
||||
-----
|
||||
|
||||
Big Number
|
||||
----------
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *isBigNumber --- ethers v3*
|
||||
|
||||
let checkBigNumber = ethers.utils.isBigNumber(value);
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *isBigNumber --- ethers v4*
|
||||
|
||||
let checkBigNumber = BigNumber.isBigNumber(value);
|
||||
|
||||
-----
|
||||
|
||||
JsonRpcProvider
|
||||
----------------
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Connecting --- ethers v3*
|
||||
|
||||
let url = "http://localhost:8545";
|
||||
let network = "rinkeby";
|
||||
let provider = new ethers.providers.JsonRpcProvider(url, network);
|
||||
|
||||
// Even if the network was wrong, this would mostly seem to work and
|
||||
// fail once the chain ID was attempted
|
||||
|
||||
// The network was a synchronous property on Provider
|
||||
let network = provider.network;
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Connecting --- ethers v4*
|
||||
|
||||
// In v4, the network is automatically determined; you may override
|
||||
// the network, but it must match, or it will fail early
|
||||
|
||||
let url = "http://localhost:8545";
|
||||
let provider = new ethers.providers.JsonRpcProvider(url);
|
||||
|
||||
// The network is now an asynchronous property:
|
||||
provider.getNetwork().then((network) => {
|
||||
// ...
|
||||
});
|
||||
|
||||
// One useful and common exception it that, if any async call from
|
||||
// the provider has ever succeeded, the synchronous network property
|
||||
// is then valid. The network property is populated before any
|
||||
// async call is made, so once an async call has finished, the network
|
||||
// property is available synchronously.
|
||||
|
||||
async function() {
|
||||
await provider.getBlockNumber();
|
||||
|
||||
let network = provider.network;
|
||||
}
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Sending Transactions --- ethers v3*
|
||||
|
||||
provider.sendTransaction(rawTransaction).then((hash) => {
|
||||
// Just a transaction hash
|
||||
});
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Sending Transactions --- ethers v4*
|
||||
|
||||
provider.sendTransaction(rawTransaction).then((transaction) => {
|
||||
// A full Transaction Response is returned
|
||||
// - from
|
||||
// - to
|
||||
// - gasLimit, gasPrice
|
||||
// - nonce
|
||||
// - r, s, v
|
||||
// - wait() => Promise that resolves the Transaction Receipt once mined
|
||||
// and rejects with an error is the stats is 0; the error
|
||||
// will have a transactionHash property as well as a
|
||||
// transaction property.
|
||||
|
||||
let hash = transaction.hash;
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
Verifying Messages
|
||||
------------------
|
||||
|
||||
The message verification was moved from a static class on the Wallet to the
|
||||
utilities, along with a few other functions of the elliptic curve cryptographic
|
||||
exposed.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *ethers v3*
|
||||
|
||||
let signingAddress = ethers.Wallet.verifyMessage(message, signature);
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *ethers v4*
|
||||
|
||||
let signingAddress = ethers.utils.verifyMessage(message, signature);
|
||||
|
||||
-----
|
||||
|
||||
Waiting for Transactions
|
||||
------------------------
|
||||
|
||||
In v3, the ``transaction.wait()`` returned a Promise which would resolve to the
|
||||
**TransactionResponse** once it is mined.
|
||||
|
||||
In v4, the ``transaction.wait()`` returned a Promise which would resolve to the
|
||||
**TransactionReceipt** once it is mined.
|
||||
|
||||
-----
|
||||
|
||||
.. eof
|
@ -1,314 +0,0 @@
|
||||
Notes
|
||||
*****
|
||||
|
||||
A few quick notes about some of the less obvious aspects of interacting with
|
||||
Ethereum in JavaScript.
|
||||
|
||||
-----
|
||||
|
||||
.. _ieee754:
|
||||
|
||||
Why can't I just use numbers?
|
||||
=============================
|
||||
|
||||
The first problem many encounter when dealing with Ethereum is the concept of numbers. Most
|
||||
common currencies are broken down with very little granularity. For example, there are only
|
||||
100 cents in a single dollar. However, there are 10\ :sup:`18` **wei** in a single
|
||||
**ether**.
|
||||
|
||||
JavaScript uses `IEEE 754 double-precision binary floating point`_ numbers to represent
|
||||
numeric values. As a result, there are *holes* in the integer set after
|
||||
9,007,199,254,740,991; which is problematic for *Ethereum* because that is only
|
||||
around 0.009 ether (in wei).
|
||||
|
||||
To demonstrate how this may be an issue in your code, consider::
|
||||
|
||||
> (Number.MAX_SAFE_INTEGER + 4 - 5) == (Number.MAX_SAFE_INTEGER - 1)
|
||||
false
|
||||
|
||||
|
||||
To remedy this, all numbers (which can be large) are stored and manipulated
|
||||
as :ref:`Big Numbers <bignumber>`.
|
||||
|
||||
The functions :ref:`parseEther( etherString ) <parseEther>` and :ref:`formatEther( wei ) <formatEther>` can be used to convert between
|
||||
string representations, which are displayed to or entered by the user and Big Number representations
|
||||
which can have mathematical operations handled safely.
|
||||
|
||||
-----
|
||||
|
||||
.. _promise:
|
||||
|
||||
Promises
|
||||
========
|
||||
|
||||
A `Promise in JavaScript`_ is an object which simplifies many aspects of dealing with
|
||||
asynchronous functions.
|
||||
|
||||
It allows a pending result to be treated in many ways as if it has already been resolved.
|
||||
|
||||
The most useful operations you will need are:
|
||||
|
||||
:sup:`Promise` . all ( promises )
|
||||
Returns a new promise that resolves once all the *promises* have resolved.
|
||||
|
||||
:sup:`prototype` . then ( onResolve, onReject )
|
||||
Returns another Promise, which once the Promise was resolved, the *onResolve*
|
||||
function will be executed and if an error occurs, *onReject* will be called.
|
||||
|
||||
If *onResolve* returns a Promise, it will be inserted into the chain of the returned
|
||||
promise. If *onResolve* throws an Error, the returned Promise will reject.
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: *Cleaning out an account*
|
||||
|
||||
var ethers = require('ethers');
|
||||
var targetAddress = "0x02F024e0882B310c6734703AB9066EdD3a10C6e0";
|
||||
|
||||
var privateKey = "0x0123456789012345678901234567890123456789012345678901234567890123";
|
||||
var wallet = new ethers.Wallet(privateKey);
|
||||
|
||||
// Promises we are interested in
|
||||
var provider = ethers.getDefaultProvider('ropsten');
|
||||
var balancePromise = provider.getBalance(wallet.address);
|
||||
var gasPricePromise = provider.getGasPrice();
|
||||
var transactionCountPromise = provider.getTransactionCount(wallet.address);
|
||||
|
||||
var allPromises = Promise.all([
|
||||
gasPricePromise,
|
||||
balancePromise,
|
||||
transactionCountPromise
|
||||
]);
|
||||
|
||||
var sendPromise = allPromises.then(function(results) {
|
||||
// This function is ONLY called once ALL promises are fulfilled
|
||||
|
||||
var gasPrice = results[0];
|
||||
var balance = results[1];
|
||||
var transactionCount = results[2];
|
||||
|
||||
// Sending a transaction to an externally owned account (EOA) is 21000 gas)
|
||||
var txFeeInWei = gasPrice.mul(21000);
|
||||
|
||||
// This will send the maximum amount (our balance minus the fee)
|
||||
var value = balance.sub(txFeeInWei);
|
||||
|
||||
var transaction = {
|
||||
to: targetAddress,
|
||||
gasPrice: gasPrice,
|
||||
gasLimit: 21000,
|
||||
nonce: transactionCount,
|
||||
|
||||
// The amount to send
|
||||
value: value,
|
||||
|
||||
// Prevent replay attacks across networks
|
||||
chainId: provider.chainId,
|
||||
};
|
||||
|
||||
var signedTransaction = wallet.sign(transaction);
|
||||
|
||||
// By returning a Promise, the sendPromise will resolve once the
|
||||
// transaction is sent
|
||||
return provider.sendTransaction(signedTransaction);
|
||||
});
|
||||
|
||||
var minedPromise = sendPromise.then(function(transaction) {
|
||||
// This will be called once the transaction is sent
|
||||
|
||||
// This promise will be resolve once the transaction has been mined.
|
||||
return provider.waitForTransaction(transaction);
|
||||
});
|
||||
|
||||
minedPromise.then(function(transaction) {
|
||||
console.log("The transaction was mined: Block " + transaction.blockNumber);
|
||||
});
|
||||
|
||||
|
||||
// Promises can be re-used for their value; it will not make the external
|
||||
// call again, and will provide the exact same result every time.
|
||||
balancePromise.then(function(balance) {
|
||||
// This *may* return before the above allPromises, since it only
|
||||
// required one external call. Keep in mind asynchronous calls can
|
||||
// be called out of order.
|
||||
console.log(balance);
|
||||
});
|
||||
|
||||
-----
|
||||
|
||||
.. _checksum-address:
|
||||
|
||||
Checksum Address
|
||||
================
|
||||
|
||||
A `checksum address`_ uses mixed case hexadecimal strings to encode the checksum
|
||||
information in the capitalization of the alphabetic characters, while remaining
|
||||
backwards compatible with non-checksum addresses.
|
||||
|
||||
Example::
|
||||
|
||||
// Valid; checksum (mixed case)
|
||||
0xCd2a3d9f938e13Cd947eC05ABC7fe734df8DD826
|
||||
|
||||
// Valid; NO checksum (no mixed case)
|
||||
0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826
|
||||
0xCD2A3D9F938E13CD947EC05ABC7FE734DF8DD826
|
||||
|
||||
// INVALID; (mixed case, but case differs from first example)
|
||||
0xDc2a3d9f938e13cd947ec05abc7fe734df8dd826
|
||||
^^
|
||||
|
||||
To convert to a checksum addresses, see :ref:`getAddress() <utils-getaddress>`.
|
||||
|
||||
.. _checksum address: https://eips.ethereum.org/EIPS/eip-55
|
||||
|
||||
-----
|
||||
|
||||
.. _icap-address:
|
||||
|
||||
ICAP Address
|
||||
============
|
||||
|
||||
The original method of adding a checksum to an Ethereum address was by using the
|
||||
a format compatible with `IBAN`_ addresses, using the country code **XE**. However,
|
||||
only addresses which have 0 as the first byte (i.e. the address begins with 0x00)
|
||||
are truly compatible with IBAN, so ICAP extends the definition to allow for 31
|
||||
alphanumeric characters (instead of the standard 30).
|
||||
|
||||
An ICAP address has the following format::
|
||||
|
||||
XE [2 digit checksum] [up to 31 alphanumeric characters]
|
||||
|
||||
To convert to an ICAP addresses, see :ref:`getIcapAddress() <utils-getaddress>`.
|
||||
|
||||
-----
|
||||
|
||||
Supported Platforms
|
||||
===================
|
||||
|
||||
The ethers.js library aims to be as inclusive as possible. People often ask, "why
|
||||
don't you use feature X or syntax Y", to which the response is usually that it
|
||||
begins to heavily restricts the potential user-base.
|
||||
|
||||
The current target for ethers.js is to support an environment which is close to ES3,
|
||||
with the addition of Object.defineProperty, which is a bit more advanced than an old
|
||||
ES3 environment, but which adds considerable safety and security to the library.
|
||||
|
||||
The phantomjs test harness (``npm run test-phantomjs``) has a handful of shims included
|
||||
in the tests/test.html, but serves as a good benchmark for what minimum features as
|
||||
supported.
|
||||
|
||||
Currently the Test Suite runs against:
|
||||
|
||||
- node 6
|
||||
- node 8
|
||||
- node 10
|
||||
- phantomjs
|
||||
|
||||
Another supported aspect is the use of paths in ``require``. A small part of the
|
||||
library may be included, for example, ``keccak256``, by using::
|
||||
|
||||
var keccak256 = require('ethers/utils/keccak256').keccak256;
|
||||
|
||||
Which means renaming files is a breaking change, and may only be done between
|
||||
major version releases. This is useful for people using older, pre-ES6
|
||||
tree-shaking, to keep their package sizes small.
|
||||
|
||||
Now that the library also supports TypeScript, another question that often comes
|
||||
up is (for example) "why are you doing runtime checks that a value is a number,
|
||||
the TypeScript compiler checks that for you". It is important to keep in mind that
|
||||
TypeScript, while a useful tool, is not the tool that everyone uses, and so for
|
||||
anyone using JavaScript sans TypeScript, the library should guarantee safety and
|
||||
correctness for them too and fail early and fail loud if anything is out of the
|
||||
ordinary.
|
||||
|
||||
-----
|
||||
|
||||
Contributing
|
||||
============
|
||||
|
||||
I fully welcome anyone to contribute to the project, and appreciate all the
|
||||
help I can get. That said, if you have ideas for a PR, please discuss them
|
||||
as an issue on GitHub first.
|
||||
|
||||
A few notes on contributing.
|
||||
|
||||
- Please read the above section on Supported Platforms.
|
||||
- An important feature of ethers.js is that it is small, which means uncommon features or large features need a great deal of discussion.
|
||||
- Since ethers.js is designed to be extensible, there are often ways to add optional packages; for example, look at the BIP39 mnemonic wordlists, which are not bundled into the browser version, but are designed to be seamlessly loaded into the browser if their functionality is required.
|
||||
- Dependencies; part A) in line with the above, "keep things small", adding a dependency is a big deal, as they often bring many other packages with them. A great deal of effort has been used to tune the build process and dependency list to keep things tight
|
||||
- Dependencies; part B) adding additional third party libraries, adds a huge attack vector fun malicious code or unexpected consequences, so adding a dependency is certainly something that needs to be very convincingly argued.
|
||||
- Dependencies; part C) part B applies to dev dependencies too. A devDependency can inject or otherwise do strange things and increases the attack vector for bugs and malicious code
|
||||
- Changing filenames or breaking backwards compatibility is a no-go for minor version changes
|
||||
- Major version changes do not happen often. We place @TODO in the source code for things that will be updated at the next version change.
|
||||
- Please use the GitHub issue system to make requests, or discuss changes you would like to make.
|
||||
- Testing is a must. It should generally take you longer to write test cases than it does the actual code.
|
||||
- All test cases must pass on all platforms supported on Travis CI.
|
||||
|
||||
-----
|
||||
|
||||
Security
|
||||
========
|
||||
|
||||
A lot of people store a lot of value in Ethereum and the code that runs it. As
|
||||
such, security is important.
|
||||
|
||||
|
||||
The GitHub and NPM Package
|
||||
--------------------------
|
||||
|
||||
The keys used to sign code on GitHub are well protected, but anyones computer
|
||||
can be compromised.
|
||||
|
||||
All services involved have two-factor authentication set up, but please keep in
|
||||
mind that bleeding-edge technology should probably not be used in production
|
||||
environments.
|
||||
|
||||
Keep in mind, however, that at the end of the day, if NPM were hacked, anything
|
||||
in the system could be replaced.
|
||||
|
||||
By using a version that is perhaps a few weeks old, providing there are no
|
||||
advisories otherwise, there has been adequate time for any compromise to have
|
||||
been broadcast.
|
||||
|
||||
Also, one of the test cases verifies the deterministic build on `Travis CI`_. **Never**
|
||||
install a version which has failed the Continuous Integration tests on Travis CI.
|
||||
|
||||
Long story short, be careful.
|
||||
|
||||
In the event of any significant issue, it will be posted on the README.md file,
|
||||
have an issue posted, with ALL CAPS in the title and will be broadcast on the
|
||||
@ethersproject twitter.
|
||||
|
||||
|
||||
Memory Hard Brute-Force Encrpyting
|
||||
----------------------------------
|
||||
|
||||
A topic that often comes up is the poor performance of decrypting Wallet.
|
||||
|
||||
While it may not be immediately obvious, this is intentional for security
|
||||
purposes.
|
||||
|
||||
If it takes the legitimate user, who knows the password 5 seconds or so to
|
||||
unlock their account, that means that an attacker must spend 5 seconds per
|
||||
password attempt, so to guess a million passwords, requires 5 million
|
||||
seconds. Client software can streamline the process by using Secure Enclaves
|
||||
or other secure local places to store the decrypted wallet to improve the
|
||||
customer experience past the first decryption.
|
||||
|
||||
|
||||
Responsible Disclosure
|
||||
----------------------
|
||||
|
||||
If you find a critical bug or security issue with ethers.js, please contact
|
||||
support@ethers.io so that we can address it before you make it public.
|
||||
You will receive credit for the discovery after it is fixed and announced. :)
|
||||
|
||||
-----
|
||||
|
||||
.. _IBAN: https://en.wikipedia.org/wiki/International_Bank_Account_Number
|
||||
.. _IEEE 754 double-precision binary floating point: https://en.wikipedia.org/wiki/Double-precision_floating-point_format
|
||||
.. _BN.js: https://github.com/indutny/bn.js/
|
||||
.. _Promise in JavaScript: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
|
||||
.. _Travis CI: https://travis-ci.org/ethers-io/ethers.js
|
||||
|
||||
.. EOF
|
@ -1,19 +0,0 @@
|
||||
Testing
|
||||
*******
|
||||
|
||||
Ethers uses a large suite of test cases to help ensure the library is as
|
||||
complete, backwards compatible and correct as possible and pass
|
||||
regression as new features are added.
|
||||
|
||||
Many of the test cases are created procedurally.
|
||||
|
||||
**@TODO:**
|
||||
Explain more here on how to run and add testcases.
|
||||
|
||||
**@TODO:**
|
||||
Post links to the testcases on IPFS (the generated test cases can takes hours to
|
||||
generate and are too large to check into GitHub)
|
||||
|
||||
-----
|
||||
|
||||
.. EOF
|
Loading…
Reference in New Issue
Block a user