@exodus/ethereum-api 0.1.0 → 0.2.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/package.json CHANGED
@@ -1,11 +1,8 @@
1
1
  {
2
2
  "name": "@exodus/ethereum-api",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Ethereum Api",
5
- "main": "lib/index.js",
6
- "files": [
7
- "lib"
8
- ],
5
+ "main": "src/index.js",
9
6
  "author": "Exodus Movement, Inc.",
10
7
  "license": "UNLICENSED",
11
8
  "homepage": "https://github.com/ExodusMovement/ethereum#readme",
@@ -14,10 +11,11 @@
14
11
  },
15
12
  "dependencies": {
16
13
  "fetchival": "0.3.3",
14
+ "make-concurrent": "4.0.0",
17
15
  "ms": "^2.1.1",
18
16
  "url": "0.10.3",
19
17
  "url-join": "4.0.0",
20
18
  "ws": "6.1.0"
21
19
  },
22
- "gitHead": "4deb44ad3f254f99315cbc777ac758f7f443c821"
20
+ "gitHead": "c04f7f85e7025762fa69cd816adf95c5811455d0"
23
21
  }
@@ -0,0 +1,49 @@
1
+ import assert from 'assert'
2
+ import request from './request'
3
+
4
+ const isValidResponseCheck = (x) =>
5
+ (x.status === '1' && x.message === 'OK') || x.message === 'No transactions found'
6
+ const _request = async (...args) => request(isValidResponseCheck, 'account', ...args)
7
+
8
+ export async function fetchBalance(address) {
9
+ const balance = await _request('balance', { address })
10
+
11
+ const isValid = /^[0-9]+$/.test(balance)
12
+ if (!isValid) throw new RangeError(`Invalid balance: ${balance}`)
13
+
14
+ return balance
15
+ }
16
+
17
+ export async function fetchTxlist(address, options) {
18
+ const params = { startblock: 0, endblock: 'latest', ...options, address }
19
+ const txlist = await _request('txlist', params)
20
+
21
+ // simple check
22
+ assert(Array.isArray(txlist), `Invalid transactions: ${txlist}`)
23
+
24
+ return txlist
25
+ }
26
+
27
+ export async function fetchTxlistinternal(address, options) {
28
+ const params = { startblock: 0, endblock: 'latest', ...options, address }
29
+ const txlist = await _request('txlistinternal', params)
30
+
31
+ // simple check
32
+ assert(Array.isArray(txlist), `Invalid transactions: ${txlist}`)
33
+
34
+ return txlist
35
+ }
36
+
37
+ export async function tokenBalance(token, address) {
38
+ const params = {
39
+ [token.length === 42 ? 'contractaddress' : 'tokenname']: token,
40
+ address,
41
+ tag: 'latest',
42
+ }
43
+ const balance = await _request('tokenbalance', params)
44
+
45
+ const isValid = /^[0-9]+$/.test(balance)
46
+ if (!isValid) throw new RangeError(`Invalid balance: ${balance}`)
47
+
48
+ return balance
49
+ }
@@ -0,0 +1,48 @@
1
+ import {
2
+ fetchBalance as _fetchBalance,
3
+ fetchTxlist as _fetchTxlist,
4
+ fetchTxlistinternal as _fetchTxlistinternal,
5
+ tokenBalance as _tokenBalance,
6
+ } from './account'
7
+ import {
8
+ sendRawTransaction as _sendRawTransaction,
9
+ getTransactionCount as _getTransactionCount,
10
+ getTransactionReceipt as _getTransactionReceipt,
11
+ estimateGas as _estimateGas,
12
+ getCode as _getCode,
13
+ gasPrice as _gasPrice,
14
+ ethCall as _ethCall,
15
+ } from './proxy'
16
+ import { setEtherscanApiKey } from './request'
17
+ import { getLogs as _getLogs } from './logs'
18
+ import createWebSocket from './ws'
19
+
20
+ export const ETHERSCAN_WS_URL = 'wss://socket.etherscan.io/wshandler'
21
+
22
+ export const fetchBalance = _fetchBalance
23
+ export const fetchTxlist = _fetchTxlist
24
+ export const fetchTxlistinternal = _fetchTxlistinternal
25
+ export const tokenBalance = _tokenBalance
26
+
27
+ export const sendRawTransaction = _sendRawTransaction
28
+ export const getTransactionCount = _getTransactionCount
29
+ export const getTransactionReceipt = _getTransactionReceipt
30
+ export const estimateGas = _estimateGas
31
+ export const getCode = _getCode
32
+ export const gasPrice = _gasPrice
33
+
34
+ export const getLogs = _getLogs
35
+
36
+ export const setApiKey = setEtherscanApiKey
37
+
38
+ export const ws = createWebSocket(ETHERSCAN_WS_URL)
39
+
40
+ export const ethCall = _ethCall
41
+
42
+ export function filterTxsSent(addr, etherscanTxs) {
43
+ return etherscanTxs.filter((tx) => tx.from.toLowerCase() === addr.toLowerCase())
44
+ }
45
+
46
+ export function filterTxsReceived(addr, etherscanTxs) {
47
+ return etherscanTxs.filter((tx) => tx.to.toLowerCase() === addr.toLowerCase())
48
+ }
@@ -0,0 +1,16 @@
1
+ import assert from 'assert'
2
+ import request from './request'
3
+
4
+ const isValidResponseCheck = (x) =>
5
+ (x.status === '1' && x.message === 'OK') || x.message === 'No records found'
6
+ const _request = async (...args) => request(isValidResponseCheck, 'logs', ...args)
7
+
8
+ export async function getLogs(address, fromBlock, toBlock, options) {
9
+ const params = { ...options, address, fromBlock, toBlock }
10
+ const events = await _request('getLogs', params)
11
+
12
+ // simple check
13
+ assert(Array.isArray(events), `Invalid transactions: ${events}`)
14
+
15
+ return events
16
+ }
@@ -0,0 +1,47 @@
1
+ import request from './request'
2
+
3
+ const isValidResponseCheck = (x) => x.result !== undefined
4
+ const _request = async (...args) => request(isValidResponseCheck, 'proxy', ...args)
5
+
6
+ export async function sendRawTransaction(data) {
7
+ const txhash = await _request('eth_sendRawTransaction', { hex: '0x' + data })
8
+
9
+ const isValidTxHash = /^0x[0-9a-fA-F]{64}$/.test(txhash)
10
+ if (!isValidTxHash) throw new Error(`Invalid tx hash: ${txhash}`)
11
+
12
+ return txhash.slice(2)
13
+ }
14
+
15
+ export async function getTransactionCount(address) {
16
+ return _request('eth_getTransactionCount', { address })
17
+ }
18
+
19
+ export async function getTransactionReceipt(txhash) {
20
+ return _request('eth_getTransactionReceipt', { txhash })
21
+ }
22
+
23
+ export async function estimateGas(data) {
24
+ return _request('eth_estimateGas', data)
25
+ }
26
+
27
+ export async function getCode(address) {
28
+ const code = await _request('eth_getCode', { address })
29
+
30
+ const isValidCode = /^0x[0-9a-fA-F]*$/.test(code) && code.length % 2 === 0
31
+ if (!isValidCode) throw new Error(`Invalid address code: ${code}`)
32
+
33
+ return code
34
+ }
35
+
36
+ export async function gasPrice() {
37
+ const price = await _request('eth_gasPrice')
38
+
39
+ const isValidPrice = /^0x[0-9a-fA-F]+$/.test(price)
40
+ if (!isValidPrice) throw new Error(`Invalid price: ${price}`)
41
+
42
+ return price
43
+ }
44
+
45
+ export async function ethCall(data) {
46
+ return _request('eth_call', data)
47
+ }
@@ -0,0 +1,25 @@
1
+ import ms from 'ms'
2
+ import makeConcurrent from 'make-concurrent'
3
+ import fetchival from 'fetchival'
4
+ // The module in desktop explicitly sets node-fetch. Do we need this?
5
+ // import fetch from '../fetch'
6
+ // fetchival.fetch = fetch
7
+
8
+ const ETHERSCAN_API_URL = 'https://api.etherscan.io/api'
9
+ const DEFAULT_ETHERSCAN_API_KEY = 'XM3VGRSNW1TMSIR14I9MVFP15X74GNHTRI'
10
+
11
+ let etherscanApiKey = DEFAULT_ETHERSCAN_API_KEY
12
+
13
+ export function setEtherscanApiKey(apiKey) {
14
+ etherscanApiKey = apiKey || DEFAULT_ETHERSCAN_API_KEY
15
+ }
16
+
17
+ export default makeConcurrent(
18
+ async function(isValidResponseCheck, module, action, params = {}) {
19
+ const queryParams = { ...params, module, action, apiKey: etherscanApiKey }
20
+ const data = await fetchival(ETHERSCAN_API_URL, { timeout: ms('15s') }).get(queryParams)
21
+ if (!isValidResponseCheck(data)) throw new Error(`Invalid response: ${JSON.stringify(data)}`)
22
+ return data.result
23
+ },
24
+ { concurrency: 3 }
25
+ )
@@ -0,0 +1,88 @@
1
+ import { EventEmitter } from 'events'
2
+ import ms from 'ms'
3
+ import WebSocket from '../websocket'
4
+
5
+ const RECONNECT_INTERVAL = ms('10s')
6
+ const PING_INTERVAL = ms('20s')
7
+
8
+ export default function createWebSocket(url) {
9
+ const addresses = new Set()
10
+ const events = new EventEmitter().setMaxListeners(20)
11
+ const pingMessage = JSON.stringify({ event: 'ping' })
12
+ let ws
13
+ let wsOpened = false
14
+ let opened = false
15
+ let openTimeoutId
16
+ let pingIntervalId
17
+
18
+ function subscribeAddress(address) {
19
+ const data = JSON.stringify({ event: 'txlist', address })
20
+ ws.send(data)
21
+ }
22
+
23
+ function onMessage(data) {
24
+ data = JSON.parse(data)
25
+ switch (data.event) {
26
+ case 'txlist':
27
+ for (const tx of data.result) events.emit(`address-${data.address}`, tx)
28
+ break
29
+
30
+ case 'subscribe-txlist':
31
+ const match = data.message.toLowerCase().match(/0x[0-9a-f]{40}/)
32
+ if (match && data.status === '1') events.emit(`address-${match[0]}-subscribed`)
33
+ else ws.close()
34
+ break
35
+ }
36
+ }
37
+
38
+ function isOpened() {
39
+ return wsOpened
40
+ }
41
+
42
+ function open() {
43
+ opened = true
44
+ clearTimeout(openTimeoutId)
45
+ if (ws) return
46
+
47
+ ws = new WebSocket(url)
48
+
49
+ ws.on('message', (data) => {
50
+ try {
51
+ onMessage(data)
52
+ } catch (err) {}
53
+ })
54
+ ws.once('open', () => {
55
+ for (const address of addresses.values()) subscribeAddress(address)
56
+ pingIntervalId = setInterval(() => ws && ws.send(pingMessage), PING_INTERVAL)
57
+ wsOpened = true
58
+ events.emit('open')
59
+ })
60
+ ws.once('close', () => {
61
+ ws = null
62
+ clearInterval(pingIntervalId)
63
+ if (opened) openTimeoutId = setTimeout(open, RECONNECT_INTERVAL)
64
+ wsOpened = false
65
+ events.emit('close')
66
+ })
67
+ }
68
+
69
+ function close() {
70
+ opened = false
71
+ clearTimeout(openTimeoutId)
72
+ if (!ws) return
73
+
74
+ ws.close()
75
+ ws = null
76
+ }
77
+
78
+ function watch(address) {
79
+ address = address.toLowerCase()
80
+
81
+ if (addresses.has(address)) return
82
+ addresses.add(address)
83
+
84
+ if (wsOpened) subscribeAddress(address)
85
+ }
86
+
87
+ return { events, isOpened, open, close, watch }
88
+ }
@@ -0,0 +1,114 @@
1
+ import urlJoin from 'url-join'
2
+ import url from 'url'
3
+ import fetchival from 'fetchival'
4
+ import ms from 'ms'
5
+ import createWebSocket from './ws'
6
+
7
+ export function create(defaultURL) {
8
+ let API_URL = defaultURL
9
+ const ws = createWebSocket(() => {
10
+ // eslint-disable-next-line
11
+ const obj = url.parse(API_URL)
12
+ obj.protocol = 'wss:'
13
+ return url.format(obj)
14
+ })
15
+
16
+ async function request(module, params = {}) {
17
+ const url = urlJoin(API_URL, module)
18
+ try {
19
+ return await fetchival(url, { timeout: ms('15s') }).get(params)
20
+ } catch (err) {
21
+ let nerr = err
22
+ if (err.response && err.response.status === 500) {
23
+ try {
24
+ const data = await err.response.json()
25
+ const msg = data.error.replace(/^RPC error \(code: -\d+\): /, '')
26
+ nerr = new Error(msg)
27
+ } catch (err) {}
28
+ }
29
+
30
+ throw nerr
31
+ }
32
+ }
33
+
34
+ return {
35
+ getURL() {
36
+ return API_URL
37
+ },
38
+
39
+ setURL(newURL) {
40
+ API_URL = newURL || defaultURL
41
+
42
+ if (ws.isCreated()) {
43
+ ws.close()
44
+ ws.open()
45
+ }
46
+ },
47
+
48
+ ws,
49
+
50
+ async getBalance(address, opts) {
51
+ opts = { startblock: 'earliest', endblock: 'pending', ...opts }
52
+ return request('balance', { address, from: opts.startblock, to: opts.endblock })
53
+ },
54
+
55
+ async getHistory(address, opts) {
56
+ opts = { startblock: 'earliest', endblock: 'pending', limit: 1000, ...opts }
57
+ return request('history', {
58
+ address,
59
+ from: opts.startblock,
60
+ to: opts.endblock,
61
+ limit: opts.limit,
62
+ })
63
+ },
64
+
65
+ async gasPrice() {
66
+ return request('proxy', { method: 'eth_gasPrice' })
67
+ },
68
+
69
+ async getTransactionCount(address, tag = 'latest') {
70
+ return request('proxy', { method: 'eth_getTransactionCount', address, tag })
71
+ },
72
+
73
+ async getTransactionByHash(hash) {
74
+ return request('proxy', { method: 'eth_getTransactionByHash', hash })
75
+ },
76
+
77
+ async getTransactionReceipt(txhash) {
78
+ return request('proxy', { method: 'eth_getTransactionReceipt', txhash })
79
+ },
80
+
81
+ async getCode(address, tag = 'latest') {
82
+ return request('proxy', { method: 'eth_getCode', address, tag })
83
+ },
84
+
85
+ async sendRawTransaction(data) {
86
+ return request('proxy', { method: 'eth_sendRawTransaction', hex: '0x' + data })
87
+ },
88
+
89
+ async estimateGas(data, tag = 'latest') {
90
+ return request('proxy', { method: 'eth_estimateGas', ...data, tag })
91
+ },
92
+
93
+ async ethCall(data, tag = 'latest') {
94
+ return request('proxy', { method: 'eth_call', ...data, tag })
95
+ },
96
+
97
+ async blockNumber() {
98
+ return request('proxy', { method: 'eth_blockNumber' })
99
+ },
100
+
101
+ async getBlockByNumber(number, isFullTransactions = false) {
102
+ return request('proxy', { method: 'eth_getBlockByNumber', number, isFullTransactions })
103
+ },
104
+
105
+ async simulateRawTransaction(rawTransaction, applyPending = true) {
106
+ rawTransaction = rawTransaction.replace('0x', '')
107
+ return request('proxy', {
108
+ method: 'debug_simulateRawTransaction',
109
+ rawTransaction,
110
+ applyPending,
111
+ })
112
+ },
113
+ }
114
+ }
@@ -0,0 +1,9 @@
1
+ import { create } from './api'
2
+
3
+ const EXODUS_ETH_SERVER_URL = 'https://eth.a.exodus.io/wallet/v1/'
4
+ const EXODUS_ETC_SERVER_URL = 'https://etc.a.exodus.io/wallet/v1/'
5
+
6
+ // allow self-signed certs
7
+ // process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
8
+ export const eth = create(EXODUS_ETH_SERVER_URL)
9
+ export const etc = create(EXODUS_ETC_SERVER_URL)
@@ -0,0 +1,89 @@
1
+ import ms from 'ms'
2
+ import { EventEmitter } from 'events'
3
+ import WebSocket from '../websocket'
4
+
5
+ const RECONNECT_INTERVAL = ms('10s')
6
+ const PING_INTERVAL = ms('10s')
7
+
8
+ export default function createWebSocket(getURL) {
9
+ const addresses = new Set()
10
+ const events = new EventEmitter()
11
+ let ws
12
+ let pingIntervalId = null
13
+ let opened = false
14
+ let openTimeoutId
15
+
16
+ function subscribeAddress(address) {
17
+ const data = JSON.stringify({ type: 'subscribe-address', address })
18
+ ws.send(data)
19
+ }
20
+
21
+ function subscribeGasPrice() {
22
+ const data = JSON.stringify({ type: 'subscribe-gasprice' })
23
+ ws.send(data)
24
+ }
25
+
26
+ function onMessage(data) {
27
+ data = JSON.parse(data)
28
+ switch (data.type) {
29
+ case 'address':
30
+ events.emit(`address-${data.address}`)
31
+ break
32
+ case 'gasprice':
33
+ events.emit('gasprice', data.gasprice)
34
+ break
35
+ }
36
+ }
37
+
38
+ function open() {
39
+ opened = true
40
+ clearTimeout(openTimeoutId)
41
+ if (ws) return
42
+
43
+ ws = new WebSocket(getURL())
44
+ ws.onerror = () => {}
45
+ ws.onmessage = (e) => {
46
+ try {
47
+ onMessage(e.data)
48
+ } catch (err) {}
49
+ }
50
+ ws.onopen = () => {
51
+ for (const address of addresses.values()) subscribeAddress(address)
52
+ subscribeGasPrice()
53
+ pingIntervalId = setInterval(() => ws.ping(), PING_INTERVAL)
54
+ events.emit('open')
55
+ }
56
+ ws.onclose = () => {
57
+ ws = null
58
+ clearInterval(pingIntervalId)
59
+ pingIntervalId = null
60
+ if (opened) openTimeoutId = setTimeout(open, RECONNECT_INTERVAL)
61
+ events.emit('close')
62
+ }
63
+ }
64
+
65
+ function close() {
66
+ opened = false
67
+ clearTimeout(openTimeoutId)
68
+ if (!ws) return
69
+
70
+ ws.onerror = null
71
+ ws.onmessage = null
72
+ ws.onopen = null
73
+ ws.onclose = null
74
+ ws.close()
75
+ ws = null
76
+ clearInterval(pingIntervalId)
77
+ pingIntervalId = null
78
+ events.emit('close')
79
+ }
80
+
81
+ function watch(address) {
82
+ if (addresses.has(address)) return
83
+ addresses.add(address)
84
+
85
+ if (ws) subscribeAddress(address)
86
+ }
87
+
88
+ return { events, open, close, watch, isCreated: () => !!ws, isOpened: () => !!pingIntervalId }
89
+ }
package/src/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import * as etherscan from './etherscan'
2
+
3
+ export * from './with-fallback'
4
+ export * from './exodus-eth-server'
5
+ export { etherscan }
@@ -0,0 +1,2 @@
1
+ /* global WebSocket */
2
+ export default WebSocket
@@ -0,0 +1,2 @@
1
+ /* global WebSocket */
2
+ export default WebSocket
@@ -0,0 +1,3 @@
1
+ import WebSocket from 'ws'
2
+
3
+ export default WebSocket
@@ -0,0 +1,14 @@
1
+ export function withFallback(fn, fn2) {
2
+ return async (...args) => {
3
+ try {
4
+ return await fn(...args)
5
+ } catch (err1) {
6
+ try {
7
+ return await fn2(...args)
8
+ } catch (err2) {
9
+ const err = new Error(`${err1.message} | ${err2.message}`)
10
+ throw Object.assign(err, { err1, err2 })
11
+ }
12
+ }
13
+ }
14
+ }
package/lib/api.js DELETED
@@ -1,170 +0,0 @@
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 DELETED
@@ -1,56 +0,0 @@
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;
@@ -1,10 +0,0 @@
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;
@@ -1,10 +0,0 @@
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;
@@ -1,13 +0,0 @@
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;
@@ -1,24 +0,0 @@
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 DELETED
@@ -1,115 +0,0 @@
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
- }