'use strict'; var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); // See: https://github.com/ethereum/wiki/wiki/JSON-RPC var networks_1 = require("./networks"); var provider_1 = require("./provider"); var address_1 = require("../utils/address"); var convert_1 = require("../utils/convert"); var utf8_1 = require("../utils/utf8"); var web_1 = require("../utils/web"); var errors = __importStar(require("../utils/errors")); function timer(timeout) { return new Promise(function (resolve) { setTimeout(function () { resolve(); }, timeout); }); } function getResult(payload) { if (payload.error) { // @TODO: not any var error = new Error(payload.error.message); error.code = payload.error.code; error.data = payload.error.data; throw error; } return payload.result; } // Convert an ethers.js transaction into a JSON-RPC transaction // - gasLimit => gas // - All values hexlified // - All numeric values zero-striped function hexlifyTransaction(transaction) { var result = {}; // Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like extra zeros. ['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(function (key) { if (transaction[key] == null) { return; } var value = convert_1.hexStripZeros(convert_1.hexlify(transaction[key])); if (key === 'gasLimit') { key = 'gas'; } result[key] = value; }); ['from', 'to', 'data'].forEach(function (key) { if (transaction[key] == null) { return; } result[key] = convert_1.hexlify(transaction[key]); }); return result; } exports.hexlifyTransaction = hexlifyTransaction; function getLowerCase(value) { if (value) { return value.toLowerCase(); } return value; } var JsonRpcSigner = /** @class */ (function () { // private _syncAddress: boolean; function JsonRpcSigner(provider, address) { errors.checkNew(this, JsonRpcSigner); this.provider = provider; // Statically attach to a given address if (address) { this._address = address; //this._syncAddress = true; } } Object.defineProperty(JsonRpcSigner.prototype, "address", { get: function () { if (!this._address) { errors.throwError('no sync sync address available; use getAddress', errors.UNSUPPORTED_OPERATION, { operation: 'address' }); } return this._address; }, enumerable: true, configurable: true }); JsonRpcSigner.prototype.getAddress = function () { if (this._address) { return Promise.resolve(this._address); } return this.provider.send('eth_accounts', []).then(function (accounts) { if (accounts.length === 0) { errors.throwError('no accounts', errors.UNSUPPORTED_OPERATION, { operation: 'getAddress' }); } return address_1.getAddress(accounts[0]); }); }; JsonRpcSigner.prototype.getBalance = function (blockTag) { var _this = this; return this.getAddress().then(function (address) { return _this.provider.getBalance(address, blockTag); }); }; JsonRpcSigner.prototype.getTransactionCount = function (blockTag) { var _this = this; return this.getAddress().then(function (address) { return _this.provider.getTransactionCount(address, blockTag); }); }; // @TODO: //sendTransaction(transaction: TransactionRequest): Promise { JsonRpcSigner.prototype.sendTransaction = function (transaction) { var _this = this; transaction = hexlifyTransaction(transaction); return this.getAddress().then(function (address) { transaction.from = address.toLowerCase(); return _this.provider.send('eth_sendTransaction', [transaction]).then(function (hash) { // @TODO: Use secp256k1 to fill this in instead... return new Promise(function (resolve, reject) { function check() { this.provider.getTransaction(hash).then(function (transaction) { if (!transaction) { setTimeout(check, 1000); return; } transaction.wait = function (timeout) { return this.provider.waitForTransaction(hash, timeout); }; resolve(transaction); }); } check(); }); }); }); }; JsonRpcSigner.prototype.signMessage = function (message) { var _this = this; var data = ((typeof (message) === 'string') ? utf8_1.toUtf8Bytes(message) : message); return this.getAddress().then(function (address) { // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign return _this.provider.send('eth_sign', [address.toLowerCase(), convert_1.hexlify(data)]); }); }; JsonRpcSigner.prototype.unlock = function (password) { var provider = this.provider; return this.getAddress().then(function (address) { return provider.send('personal_unlockAccount', [address.toLowerCase(), password, null]); }); }; return JsonRpcSigner; }()); exports.JsonRpcSigner = JsonRpcSigner; var JsonRpcProvider = /** @class */ (function (_super) { __extends(JsonRpcProvider, _super); function JsonRpcProvider(url, network) { var _this = this; // One parameter, but it is a network name, so swap it with the URL if (typeof (url) === 'string') { if (network === null && networks_1.getNetwork(url)) { network = url; url = null; } } _this = _super.call(this, network) || this; errors.checkNew(_this, JsonRpcProvider); // Default URL if (!url) { url = 'http://localhost:8545'; } if (typeof (url) === 'string') { _this.connection = { url: url }; } else { _this.connection = url; } // The network is unknown, query the JSON-RPC for it if (!_this.network) { _this.ready = new Promise(function (resolve, reject) { setTimeout(function () { _this.send('net_version', []).then(function (result) { var chainId = parseInt(result); var network = networks_1.getNetwork(chainId); if (network) { return resolve(network); } resolve({ name: 'custom', chainId: chainId }); }); }); }); } return _this; } JsonRpcProvider.prototype.getSigner = function (address) { return new JsonRpcSigner(this, address); }; JsonRpcProvider.prototype.listAccounts = function () { return this.send('eth_accounts', []).then(function (accounts) { return accounts.map(function (a) { return address_1.getAddress(a); }); }); }; JsonRpcProvider.prototype.send = function (method, params) { var request = { method: method, params: params, id: 42, jsonrpc: "2.0" }; return web_1.fetchJson(this.connection, JSON.stringify(request), getResult); }; JsonRpcProvider.prototype.perform = function (method, params) { switch (method) { case 'getBlockNumber': return this.send('eth_blockNumber', []); case 'getGasPrice': return this.send('eth_gasPrice', []); case 'getBalance': return this.send('eth_getBalance', [getLowerCase(params.address), params.blockTag]); case 'getTransactionCount': return this.send('eth_getTransactionCount', [getLowerCase(params.address), params.blockTag]); case 'getCode': return this.send('eth_getCode', [getLowerCase(params.address), params.blockTag]); case 'getStorageAt': return this.send('eth_getStorageAt', [getLowerCase(params.address), params.position, params.blockTag]); case 'sendTransaction': return this.send('eth_sendRawTransaction', [params.signedTransaction]); case 'getBlock': if (params.blockTag) { return this.send('eth_getBlockByNumber', [params.blockTag, false]); } else if (params.blockHash) { return this.send('eth_getBlockByHash', [params.blockHash, false]); } return Promise.reject(new Error('invalid block tag or block hash')); case 'getTransaction': return this.send('eth_getTransactionByHash', [params.transactionHash]); case 'getTransactionReceipt': return this.send('eth_getTransactionReceipt', [params.transactionHash]); case 'call': return this.send('eth_call', [hexlifyTransaction(params.transaction), 'latest']); case 'estimateGas': return this.send('eth_estimateGas', [hexlifyTransaction(params.transaction)]); case 'getLogs': if (params.filter && params.filter.address != null) { params.filter.address = getLowerCase(params.filter.address); } return this.send('eth_getLogs', [params.filter]); default: break; } errors.throwError(method + ' not implemented', errors.NOT_IMPLEMENTED, { operation: method }); return null; }; JsonRpcProvider.prototype._startPending = function () { if (this._pendingFilter != null) { return; } var self = this; var pendingFilter = this.send('eth_newPendingTransactionFilter', []); this._pendingFilter = pendingFilter; pendingFilter.then(function (filterId) { function poll() { self.send('eth_getFilterChanges', [filterId]).then(function (hashes) { if (self._pendingFilter != pendingFilter) { return null; } var seq = Promise.resolve(); hashes.forEach(function (hash) { self._emitted['t:' + hash.toLowerCase()] = 'pending'; seq = seq.then(function () { return self.getTransaction(hash).then(function (tx) { self.emit('pending', tx); }); }); }); return seq.then(function () { return timer(1000); }); }).then(function () { if (self._pendingFilter != pendingFilter) { self.send('eth_uninstallFilter', [filterId]); return; } setTimeout(function () { poll(); }, 0); }); } poll(); return filterId; }); }; JsonRpcProvider.prototype._stopPending = function () { this._pendingFilter = null; }; return JsonRpcProvider; }(provider_1.Provider)); exports.JsonRpcProvider = JsonRpcProvider;