@exodus/ethereum-api 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.md ADDED
File without changes
package/lib/api.js ADDED
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.create = create;
7
+
8
+ var _urlJoin = _interopRequireDefault(require("url-join"));
9
+
10
+ var _url = _interopRequireDefault(require("url"));
11
+
12
+ var _fetchival = _interopRequireDefault(require("fetchival"));
13
+
14
+ var _ms = _interopRequireDefault(require("ms"));
15
+
16
+ var _ws = _interopRequireDefault(require("./ws"));
17
+
18
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
+
20
+ function create(defaultURL) {
21
+ let API_URL = defaultURL;
22
+ const ws = (0, _ws.default)(() => {
23
+ // eslint-disable-next-line
24
+ const obj = _url.default.parse(API_URL);
25
+
26
+ obj.protocol = 'wss:';
27
+ return _url.default.format(obj);
28
+ });
29
+
30
+ async function request(module, params = {}) {
31
+ const url = (0, _urlJoin.default)(API_URL, module);
32
+
33
+ try {
34
+ return await (0, _fetchival.default)(url, {
35
+ timeout: (0, _ms.default)('15s')
36
+ }).get(params);
37
+ } catch (err) {
38
+ let nerr = err;
39
+
40
+ if (err.response && err.response.status === 500) {
41
+ try {
42
+ const data = await err.response.json();
43
+ const msg = data.error.replace(/^RPC error \(code: -\d+\): /, '');
44
+ nerr = new Error(msg);
45
+ } catch (err) {}
46
+ }
47
+
48
+ throw nerr;
49
+ }
50
+ }
51
+
52
+ return {
53
+ getURL() {
54
+ return API_URL;
55
+ },
56
+
57
+ setURL(newURL) {
58
+ API_URL = newURL || defaultURL;
59
+
60
+ if (ws.isCreated()) {
61
+ ws.close();
62
+ ws.open();
63
+ }
64
+ },
65
+
66
+ ws,
67
+
68
+ async getBalance(address, opts) {
69
+ opts = {
70
+ startblock: 'earliest',
71
+ endblock: 'pending',
72
+ ...opts
73
+ };
74
+ return request('balance', {
75
+ address,
76
+ from: opts.startblock,
77
+ to: opts.endblock
78
+ });
79
+ },
80
+
81
+ async getHistory(address, opts) {
82
+ opts = {
83
+ startblock: 'earliest',
84
+ endblock: 'pending',
85
+ limit: 1000,
86
+ ...opts
87
+ };
88
+ return request('history', {
89
+ address,
90
+ from: opts.startblock,
91
+ to: opts.endblock,
92
+ limit: opts.limit
93
+ });
94
+ },
95
+
96
+ async gasPrice() {
97
+ return request('proxy', {
98
+ method: 'eth_gasPrice'
99
+ });
100
+ },
101
+
102
+ async getTransactionCount(address, tag = 'latest') {
103
+ return request('proxy', {
104
+ method: 'eth_getTransactionCount',
105
+ address,
106
+ tag
107
+ });
108
+ },
109
+
110
+ async getTransactionByHash(hash) {
111
+ return request('proxy', {
112
+ method: 'eth_getTransactionByHash',
113
+ hash
114
+ });
115
+ },
116
+
117
+ async getTransactionReceipt(txhash) {
118
+ return request('proxy', {
119
+ method: 'eth_getTransactionReceipt',
120
+ txhash
121
+ });
122
+ },
123
+
124
+ async getCode(address, tag = 'latest') {
125
+ return request('proxy', {
126
+ method: 'eth_getCode',
127
+ address,
128
+ tag
129
+ });
130
+ },
131
+
132
+ async sendRawTransaction(data) {
133
+ return request('proxy', {
134
+ method: 'eth_sendRawTransaction',
135
+ hex: '0x' + data
136
+ });
137
+ },
138
+
139
+ async estimateGas(data, tag = 'latest') {
140
+ return request('proxy', {
141
+ method: 'eth_estimateGas',
142
+ ...data,
143
+ tag
144
+ });
145
+ },
146
+
147
+ async ethCall(data, tag = 'latest') {
148
+ return request('proxy', {
149
+ method: 'eth_call',
150
+ ...data,
151
+ tag
152
+ });
153
+ },
154
+
155
+ async blockNumber() {
156
+ return request('proxy', {
157
+ method: 'eth_blockNumber'
158
+ });
159
+ },
160
+
161
+ async getBlockByNumber(number, isFullTransactions = false) {
162
+ return request('proxy', {
163
+ method: 'eth_getBlockByNumber',
164
+ number,
165
+ isFullTransactions
166
+ });
167
+ }
168
+
169
+ };
170
+ }
package/lib/index.js ADDED
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var _exportNames = {
7
+ eth: true,
8
+ etc: true,
9
+ createWebSocket: true
10
+ };
11
+ Object.defineProperty(exports, "createWebSocket", {
12
+ enumerable: true,
13
+ get: function () {
14
+ return _ws.default;
15
+ }
16
+ });
17
+ exports.etc = exports.eth = void 0;
18
+
19
+ var _api = require("./api");
20
+
21
+ Object.keys(_api).forEach(function (key) {
22
+ if (key === "default" || key === "__esModule") return;
23
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
24
+ Object.defineProperty(exports, key, {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _api[key];
28
+ }
29
+ });
30
+ });
31
+
32
+ var _withFallback = require("./with-fallback");
33
+
34
+ Object.keys(_withFallback).forEach(function (key) {
35
+ if (key === "default" || key === "__esModule") return;
36
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
37
+ Object.defineProperty(exports, key, {
38
+ enumerable: true,
39
+ get: function () {
40
+ return _withFallback[key];
41
+ }
42
+ });
43
+ });
44
+
45
+ var _ws = _interopRequireDefault(require("./ws"));
46
+
47
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
48
+
49
+ const EXODUS_ETH_SERVER_URL = 'https://eth.a.exodus.io/wallet/v1/';
50
+ const EXODUS_ETC_SERVER_URL = 'https://etc.a.exodus.io/wallet/v1/'; // allow self-signed certs
51
+ // process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
52
+
53
+ const eth = (0, _api.create)(EXODUS_ETH_SERVER_URL);
54
+ exports.eth = eth;
55
+ const etc = (0, _api.create)(EXODUS_ETC_SERVER_URL);
56
+ exports.etc = etc;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ /* global WebSocket */
9
+ var _default = WebSocket;
10
+ exports.default = _default;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ /* global WebSocket */
9
+ var _default = WebSocket;
10
+ exports.default = _default;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ var _ws = _interopRequireDefault(require("ws"));
9
+
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
+
12
+ var _default = _ws.default;
13
+ exports.default = _default;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.withFallback = withFallback;
7
+
8
+ function withFallback(fn, fn2) {
9
+ return async (...args) => {
10
+ try {
11
+ return await fn(...args);
12
+ } catch (err1) {
13
+ try {
14
+ return await fn2(...args);
15
+ } catch (err2) {
16
+ const err = new Error(`${err1.message} | ${err2.message}`);
17
+ throw Object.assign(err, {
18
+ err1,
19
+ err2
20
+ });
21
+ }
22
+ }
23
+ };
24
+ }
package/lib/ws.js ADDED
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = createWebSocket;
7
+
8
+ var _events = require("events");
9
+
10
+ var _websocket = _interopRequireDefault(require("./websocket"));
11
+
12
+ var _ms = _interopRequireDefault(require("ms"));
13
+
14
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
+
16
+ const RECONNECT_INTERVAL = (0, _ms.default)('10s');
17
+
18
+ function createWebSocket(getURL) {
19
+ const addresses = new Set();
20
+ const events = new _events.EventEmitter();
21
+ let ws;
22
+ let pingIntervalId = null;
23
+ let opened = false;
24
+ let openTimeoutId;
25
+
26
+ function subscribeAddress(address) {
27
+ const data = JSON.stringify({
28
+ type: 'subscribe-address',
29
+ address
30
+ });
31
+ ws.send(data);
32
+ }
33
+
34
+ function subscribeGasPrice() {
35
+ const data = JSON.stringify({
36
+ type: 'subscribe-gasprice'
37
+ });
38
+ ws.send(data);
39
+ }
40
+
41
+ function onMessage(data) {
42
+ data = JSON.parse(data);
43
+
44
+ switch (data.type) {
45
+ case 'address':
46
+ events.emit(`address-${data.address}`);
47
+ break;
48
+
49
+ case 'gasprice':
50
+ events.emit('gasprice', data.gasprice);
51
+ break;
52
+ }
53
+ }
54
+
55
+ function open() {
56
+ opened = true;
57
+ clearTimeout(openTimeoutId);
58
+ if (ws) return;
59
+ ws = new _websocket.default(getURL());
60
+
61
+ ws.onerror = () => {};
62
+
63
+ ws.onmessage = e => {
64
+ try {
65
+ onMessage(e.data);
66
+ } catch (err) {}
67
+ };
68
+
69
+ ws.onopen = () => {
70
+ for (const address of addresses.values()) subscribeAddress(address);
71
+
72
+ subscribeGasPrice();
73
+ pingIntervalId = setInterval(() => ws.ping(), (0, _ms.default)('10s'));
74
+ events.emit('open');
75
+ };
76
+
77
+ ws.onclose = () => {
78
+ ws = null;
79
+ clearInterval(pingIntervalId);
80
+ pingIntervalId = null;
81
+ if (opened) openTimeoutId = setTimeout(open, RECONNECT_INTERVAL);
82
+ events.emit('close');
83
+ };
84
+ }
85
+
86
+ function close() {
87
+ opened = false;
88
+ clearTimeout(openTimeoutId);
89
+ if (!ws) return;
90
+ ws.onerror = null;
91
+ ws.onmessage = null;
92
+ ws.onopen = null;
93
+ ws.onclose = null;
94
+ ws.close();
95
+ ws = null;
96
+ clearInterval(pingIntervalId);
97
+ pingIntervalId = null;
98
+ events.emit('close');
99
+ }
100
+
101
+ function watch(address) {
102
+ if (addresses.has(address)) return;
103
+ addresses.add(address);
104
+ if (ws) subscribeAddress(address);
105
+ }
106
+
107
+ return {
108
+ events,
109
+ open,
110
+ close,
111
+ watch,
112
+ isCreated: () => !!ws,
113
+ isOpened: () => !!pingIntervalId
114
+ };
115
+ }
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@exodus/ethereum-api",
3
+ "version": "0.1.0",
4
+ "description": "Ethereum Api",
5
+ "main": "lib/index.js",
6
+ "files": [
7
+ "lib"
8
+ ],
9
+ "author": "Exodus Movement, Inc.",
10
+ "license": "UNLICENSED",
11
+ "homepage": "https://github.com/ExodusMovement/ethereum#readme",
12
+ "publishConfig": {
13
+ "access": "restricted"
14
+ },
15
+ "dependencies": {
16
+ "fetchival": "0.3.3",
17
+ "ms": "^2.1.1",
18
+ "url": "0.10.3",
19
+ "url-join": "4.0.0",
20
+ "ws": "6.1.0"
21
+ },
22
+ "gitHead": "4deb44ad3f254f99315cbc777ac758f7f443c821"
23
+ }