@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 +0 -0
- package/lib/api.js +170 -0
- package/lib/index.js +56 -0
- package/lib/websocket/index.android.js +10 -0
- package/lib/websocket/index.ios.js +10 -0
- package/lib/websocket/index.js +13 -0
- package/lib/with-fallback.js +24 -0
- package/lib/ws.js +115 -0
- package/package.json +23 -0
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,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
|
+
}
|