@instadapp/interop-x 0.0.0-dev.b5a9a93 → 0.0.0-dev.b70f25f

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. package/dist/package.json +1 -1
  2. package/dist/src/api/index.js +1 -1
  3. package/dist/src/index.js +6 -2
  4. package/dist/src/net/peer/index.js +2 -1
  5. package/dist/src/net/pool/index.js +7 -2
  6. package/dist/src/net/protocol/dial/SignatureDialProtocol.1.js +28 -0
  7. package/dist/src/net/protocol/index.js +11 -0
  8. package/dist/src/tasks/AutoUpdateTask.js +8 -8
  9. package/dist/src/tasks/BaseTask.js +7 -3
  10. package/dist/src/tasks/InteropBridge/ProcessWithdrawEvents.js +0 -1
  11. package/dist/src/tasks/InteropBridge/SyncWithdrawEvents.js +0 -1
  12. package/dist/src/tasks/InteropXGateway/ProcessDepositEvents.js +0 -1
  13. package/dist/src/tasks/InteropXGateway/SyncDepositEvents.js +0 -1
  14. package/dist/src/tasks/Transactions/SyncTransactionStatusTask.js +53 -0
  15. package/dist/src/tasks/index.js +2 -0
  16. package/dist/src/utils/index.js +14 -4
  17. package/package.json +1 -1
  18. package/src/api/index.ts +1 -1
  19. package/src/index.ts +7 -1
  20. package/src/net/peer/index.ts +2 -1
  21. package/src/net/pool/index.ts +7 -3
  22. package/src/net/protocol/dial/SignatureDialProtocol.1.ts +31 -0
  23. package/src/net/protocol/index.ts +12 -0
  24. package/src/tasks/AutoUpdateTask.ts +10 -9
  25. package/src/tasks/BaseTask.ts +8 -3
  26. package/src/tasks/InteropBridge/ProcessWithdrawEvents.ts +0 -2
  27. package/src/tasks/InteropBridge/SyncWithdrawEvents.ts +0 -2
  28. package/src/tasks/InteropXGateway/ProcessDepositEvents.ts +0 -2
  29. package/src/tasks/InteropXGateway/SyncDepositEvents.ts +0 -2
  30. package/src/tasks/Transactions/SyncTransactionStatusTask.ts +65 -0
  31. package/src/tasks/index.ts +2 -0
  32. package/src/utils/index.ts +15 -3
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instadapp/interop-x",
3
- "version": "0.0.0-dev.b5a9a93",
3
+ "version": "0.0.0-dev.b70f25f",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "engines": {
@@ -26,7 +26,7 @@ const startApiServer = async () => {
26
26
  logger.log(`RPC Server listening at http://${HOST}:${PORT}`);
27
27
  }
28
28
  catch (err) {
29
- logger.error(err);
29
+ logger.error(err.message);
30
30
  process.exit(1);
31
31
  }
32
32
  };
package/dist/src/index.js CHANGED
@@ -37,7 +37,7 @@ if (process.argv.at(-1) === 'help') {
37
37
  printUsage();
38
38
  process.exit(0);
39
39
  }
40
- const GIT_SHORT_HASH = 'b5a9a93';
40
+ const GIT_SHORT_HASH = 'b70f25f';
41
41
  if (process.argv.at(-1) === 'version') {
42
42
  console.log(`Interop X Node (v${package_json_1.default.version} - rev.${GIT_SHORT_HASH})`);
43
43
  process.exit(0);
@@ -60,6 +60,7 @@ const tasks_1 = require("@/tasks");
60
60
  const net_1 = require("@/net");
61
61
  const api_1 = require("@/api");
62
62
  const db_1 = require("./db");
63
+ const utils_1 = require("./utils");
63
64
  async function main() {
64
65
  (0, net_1.startPeer)({});
65
66
  const tasks = new tasks_1.Tasks();
@@ -68,7 +69,10 @@ async function main() {
68
69
  net_1.protocol.on('TransactionStatus', async (payload) => {
69
70
  if (!net_1.peerPool.isLeadNode(payload.peerId)) {
70
71
  const peer = net_1.peerPool.getPeer(payload.peerId);
71
- logger.info(`ignored transaction status from ${payload.peerId} ${peer === null || peer === void 0 ? void 0 : peer.publicAddress} `);
72
+ if (!peer) {
73
+ return;
74
+ }
75
+ logger.info(`ignored transaction status from ${payload.peerId} ${(0, utils_1.shortenHash)(peer.publicAddress)} `);
72
76
  return;
73
77
  }
74
78
  const transaction = await db_1.Transaction.findOne({ where: { transactionHash: payload.data.transactionHash } });
@@ -23,6 +23,7 @@ const libp2p_kad_dht_1 = __importDefault(require("libp2p-kad-dht"));
23
23
  const libp2p_pubsub_peer_discovery_1 = __importDefault(require("libp2p-pubsub-peer-discovery"));
24
24
  const net_1 = require("@/net");
25
25
  const config_1 = __importDefault(require("@/config"));
26
+ const chalk_1 = __importDefault(require("chalk"));
26
27
  const logger = new logger_1.default("Peer");
27
28
  let node;
28
29
  // Known peers addresses
@@ -77,7 +78,7 @@ const startPeer = async ({}) => {
77
78
  persistence: true,
78
79
  },
79
80
  });
80
- logger.info("Peer ID:", node.peerId.toB58String());
81
+ logger.info("Peer ID:", chalk_1.default.bold(node.peerId.toB58String()));
81
82
  await node.start();
82
83
  net_1.protocol.start({
83
84
  libp2p: node
@@ -8,6 +8,8 @@ const types_1 = require("@/types");
8
8
  const config_1 = __importDefault(require("@/config"));
9
9
  const logger_1 = __importDefault(require("@/logger"));
10
10
  const utils_1 = require("ethers/lib/utils");
11
+ const utils_2 = require("@/utils");
12
+ const chalk_1 = __importDefault(require("chalk"));
11
13
  const logger = new logger_1.default('PeerPool');
12
14
  class PeerPool {
13
15
  constructor() {
@@ -71,7 +73,7 @@ class PeerPool {
71
73
  peer.pooled = true;
72
74
  if (newPeer) {
73
75
  config_1.default.events.emit(types_1.Event.POOL_PEER_ADDED, peer);
74
- logger.info(`Peer ${peer.id} with address ${peer.publicAddress} added to pool`);
76
+ logger.info(`Peer ${chalk_1.default.bold((0, utils_2.shortenHash)(peer.id, 16))} with address ${chalk_1.default.bold((0, utils_2.shortenHash)(peer.publicAddress))} added to pool`);
75
77
  }
76
78
  }
77
79
  }
@@ -85,7 +87,7 @@ class PeerPool {
85
87
  if (this.pool.delete(peer.id)) {
86
88
  peer.pooled = false;
87
89
  config_1.default.events.emit(types_1.Event.POOL_PEER_REMOVED, peer);
88
- logger.info(`Peer ${peer.id} with address ${peer.publicAddress} removed from pool`);
90
+ logger.info(`Peer ${chalk_1.default.bold((0, utils_2.shortenHash)(peer.id, 16))} with address ${chalk_1.default.bold((0, utils_2.shortenHash)(peer.publicAddress))} removed from pool`);
89
91
  }
90
92
  }
91
93
  }
@@ -111,6 +113,9 @@ class PeerPool {
111
113
  }
112
114
  return (0, utils_1.getAddress)(peer.publicAddress) === (0, utils_1.getAddress)(config_1.default.leadNodeAddress);
113
115
  }
116
+ getLeadPeer() {
117
+ return this.peers.find((p) => this.isLeadNode(p.id));
118
+ }
114
119
  cleanup() {
115
120
  // let compDate = Date.now() - this.PEERS_CLEANUP_TIME_LIMIT * 60
116
121
  // this.peers.forEach((peerInfo) => {
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TransactionStatusDialProtocol = void 0;
4
+ const BaseDialProtocol_1 = require("./BaseDialProtocol");
5
+ const db_1 = require("@/db");
6
+ class TransactionStatusDialProtocol extends BaseDialProtocol_1.BaseDialProtocol {
7
+ constructor(libp2p) {
8
+ super(libp2p, '/interop-x/transaction-status');
9
+ this.timeout = 30000;
10
+ }
11
+ async response(transactionHash) {
12
+ const transaction = await db_1.Transaction.findOne({ where: { transactionHash } });
13
+ if (!transaction) {
14
+ return null;
15
+ }
16
+ return {
17
+ transactionHash: transaction.transactionHash,
18
+ sourceStatus: transaction.sourceStatus,
19
+ sourceTransactionHash: transaction.sourceTransactionHash,
20
+ sourceErrors: transaction.sourceErrors,
21
+ targetStatus: transaction.targetStatus,
22
+ targetTransactionHash: transaction.targetTransactionHash,
23
+ targetErrors: transaction.targetErrors,
24
+ status: transaction.status,
25
+ };
26
+ }
27
+ }
28
+ exports.TransactionStatusDialProtocol = TransactionStatusDialProtocol;
@@ -10,6 +10,7 @@ const SignatureDialProtocol_1 = require("./dial/SignatureDialProtocol");
10
10
  const __1 = require("..");
11
11
  const config_1 = __importDefault(require("@/config"));
12
12
  const types_1 = require("@/types");
13
+ const SignatureDialProtocol_1_1 = require("./dial/SignatureDialProtocol.1");
13
14
  class Protocol extends stream_1.EventEmitter {
14
15
  constructor() {
15
16
  super(...arguments);
@@ -64,6 +65,7 @@ class Protocol extends stream_1.EventEmitter {
64
65
  });
65
66
  });
66
67
  this.signature = new SignatureDialProtocol_1.SignatureDialProtocol(this.libp2p);
68
+ this.transactionStatus = new SignatureDialProtocol_1_1.TransactionStatusDialProtocol(this.libp2p);
67
69
  }
68
70
  init() {
69
71
  this.libp2p.pubsub.subscribe(this.topic);
@@ -117,5 +119,14 @@ class Protocol extends stream_1.EventEmitter {
117
119
  return [];
118
120
  }
119
121
  }
122
+ async requestTransactionStatus(transactionHash, peerId) {
123
+ try {
124
+ return await this.transactionStatus.send(transactionHash, peerId);
125
+ }
126
+ catch (error) {
127
+ console.log(error);
128
+ return null;
129
+ }
130
+ }
120
131
  }
121
132
  exports.protocol = new Protocol();
@@ -9,6 +9,9 @@ const utils_1 = require("@/utils");
9
9
  const await_spawn_1 = __importDefault(require("await-spawn"));
10
10
  const config_1 = __importDefault(require("@/config"));
11
11
  const waait_1 = __importDefault(require("waait"));
12
+ const package_json_1 = __importDefault(require("../../package.json"));
13
+ const currentVersion = package_json_1.default.version;
14
+ const packageName = package_json_1.default.name;
12
15
  class AutoUpdateTask extends BaseTask_1.BaseTask {
13
16
  constructor() {
14
17
  super({
@@ -19,23 +22,20 @@ class AutoUpdateTask extends BaseTask_1.BaseTask {
19
22
  prePollHandler() {
20
23
  return config_1.default.autoUpdate && !config_1.default.isLeadNode();
21
24
  }
22
- getCurrentVersion() {
23
- return require('../../package.json').version;
24
- }
25
25
  async pollHandler() {
26
26
  const { data } = await utils_1.http.get('https://registry.npmjs.org/@instadapp/interop-x');
27
27
  const version = data['dist-tags'].latest;
28
- const currentVersion = this.getCurrentVersion();
29
28
  if (version === currentVersion) {
30
29
  return;
31
30
  }
32
31
  this.logger.warn(`New version ${version} available.`);
32
+ this.logger.info('Updating...');
33
33
  await (0, await_spawn_1.default)('npm', ['-g', 'install', '@instadapp/interop-x', '-f']);
34
34
  await (0, waait_1.default)(5000);
35
- if (currentVersion === this.getCurrentVersion()) {
36
- this.logger.warn(`failed to install ${version}, retrying in 5 minutes`);
37
- return;
38
- }
35
+ // if (currentVersion === getCurrentVersion()) {
36
+ // this.logger.warn(`failed to install ${version}, retrying in 5 minutes`)
37
+ // return;
38
+ // }
39
39
  this.logger.warn(`Installed version ${version}`);
40
40
  this.logger.warn(`Restarting...`);
41
41
  (0, await_spawn_1.default)(process.argv[0], process.argv.slice(1), {
@@ -14,6 +14,7 @@ class BaseTask extends events_1.default {
14
14
  this.started = false;
15
15
  this.pollIntervalMs = 10 * 1000;
16
16
  this.leadNodeOnly = false;
17
+ this.exceptLeadNode = false;
17
18
  this.logger = logger !== null && logger !== void 0 ? logger : new logger_1.default('BaseTask');
18
19
  }
19
20
  async pollCheck() {
@@ -34,10 +35,13 @@ class BaseTask extends events_1.default {
34
35
  }
35
36
  }
36
37
  prePollHandler() {
37
- if (!this.leadNodeOnly) {
38
- return true;
38
+ if (this.exceptLeadNode) {
39
+ return !config_1.default.isLeadNode();
39
40
  }
40
- return config_1.default.isLeadNode();
41
+ if (this.leadNodeOnly) {
42
+ return config_1.default.isLeadNode();
43
+ }
44
+ return true;
41
45
  }
42
46
  async pollHandler() {
43
47
  this.logger.warn('pollHandler not implemented');
@@ -139,7 +139,6 @@ class ProcessWithdrawEvents extends BaseTask_1.BaseTask {
139
139
  }
140
140
  }
141
141
  async start() {
142
- this.logger.info(`Starting execution watcher on interop chain`);
143
142
  this.provider = new ethers_1.ethers.providers.JsonRpcProvider((0, utils_1.getRpcProviderUrl)(this.chainId));
144
143
  await super.start();
145
144
  }
@@ -61,7 +61,6 @@ class SyncWithdrawEvents extends BaseTask_1.BaseTask {
61
61
  this.logger.info(`${processedEvents} events processed`);
62
62
  }
63
63
  async start() {
64
- this.logger.info(`Starting execution watcher on interop chain`);
65
64
  this.provider = new ethers_1.ethers.providers.JsonRpcProvider((0, utils_1.getRpcProviderUrl)(this.chainId));
66
65
  this.contract = (0, utils_1.getContract)(this.itokenAddress, abi_1.default.interopBridgeToken, new ethers_1.ethers.Wallet(config_1.default.privateKey, this.provider));
67
66
  await super.start();
@@ -140,7 +140,6 @@ class ProcessDepositEvents extends BaseTask_1.BaseTask {
140
140
  net_1.protocol.sendTransaction(transaction);
141
141
  }
142
142
  async start() {
143
- this.logger.info(`Starting execution watcher on interop chain`);
144
143
  this.contractAddress = constants_1.addresses[this.chainId].interopXGateway;
145
144
  this.provider = new ethers_1.ethers.providers.JsonRpcProvider((0, utils_1.getRpcProviderUrl)(this.chainId));
146
145
  this.contract = (0, utils_1.getContract)(this.contractAddress, abi_1.default.interopXGateway, new ethers_1.ethers.Wallet(config_1.default.privateKey, this.provider));
@@ -65,7 +65,6 @@ class SyncDepositEvents extends BaseTask_1.BaseTask {
65
65
  this.logger.info(`${processedEvents} events processed`);
66
66
  }
67
67
  async start() {
68
- this.logger.info(`Starting execution watcher on interop chain`);
69
68
  this.contractAddress = constants_1.addresses[this.chainId].interopXGateway;
70
69
  this.provider = new ethers_1.ethers.providers.JsonRpcProvider((0, utils_1.getRpcProviderUrl)(this.chainId));
71
70
  this.contract = (0, utils_1.getContract)(this.contractAddress, abi_1.default.interopXGateway, new ethers_1.ethers.Wallet(config_1.default.privateKey, this.provider));
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const BaseTask_1 = require("../BaseTask");
7
+ const logger_1 = __importDefault(require("@/logger"));
8
+ const net_1 = require("@/net");
9
+ const db_1 = require("@/db");
10
+ const sequelize_1 = require("sequelize");
11
+ class SyncTransactionStatusTask extends BaseTask_1.BaseTask {
12
+ constructor() {
13
+ super({
14
+ logger: new logger_1.default("SyncTransactionStatusTask"),
15
+ });
16
+ this.pollIntervalMs = 60 * 1000;
17
+ this.exceptLeadNode = true;
18
+ }
19
+ async pollHandler() {
20
+ // if transaction is pending for more than 1 hour, check lead node for status
21
+ const leadNode = net_1.peerPool.getLeadPeer();
22
+ if (!leadNode) {
23
+ return;
24
+ }
25
+ const transaction = await db_1.Transaction.findOne({
26
+ where: {
27
+ status: 'pending',
28
+ sourceCreatedAt: {
29
+ [sequelize_1.Op.gte]: new Date(Date.now() - 60 * 60 * 1000),
30
+ },
31
+ }
32
+ });
33
+ if (!transaction) {
34
+ return;
35
+ }
36
+ this.logger.info(`Requesting transaction status for ${transaction.transactionHash}`);
37
+ const transactionStatus = await net_1.protocol.requestTransactionStatus(transaction.transactionHash, leadNode.id);
38
+ if (!transactionStatus) {
39
+ return;
40
+ }
41
+ this.logger.info(`Received transaction status for ${transaction.transactionHash}`);
42
+ transaction.sourceStatus = transactionStatus.sourceStatus;
43
+ transaction.sourceTransactionHash = transactionStatus.sourceTransactionHash;
44
+ transaction.sourceErrors = transactionStatus.sourceErrors;
45
+ transaction.targetStatus = transactionStatus.targetStatus;
46
+ transaction.targetTransactionHash = transactionStatus.targetTransactionHash;
47
+ transaction.targetErrors = transactionStatus.targetErrors;
48
+ transaction.status = transactionStatus.status;
49
+ await transaction.save();
50
+ this.logger.info(`Updated transaction status for ${transaction.transactionHash}`);
51
+ }
52
+ }
53
+ exports.default = SyncTransactionStatusTask;
@@ -9,9 +9,11 @@ const SyncDepositEvents_1 = __importDefault(require("./InteropXGateway/SyncDepos
9
9
  const SyncWithdrawEvents_1 = __importDefault(require("./InteropBridge/SyncWithdrawEvents"));
10
10
  const ProcessWithdrawEvents_1 = __importDefault(require("./InteropBridge/ProcessWithdrawEvents"));
11
11
  const AutoUpdateTask_1 = __importDefault(require("./AutoUpdateTask"));
12
+ const SyncTransactionStatusTask_1 = __importDefault(require("./Transactions/SyncTransactionStatusTask"));
12
13
  class Tasks {
13
14
  constructor() {
14
15
  this.tasks = [
16
+ new SyncTransactionStatusTask_1.default(),
15
17
  new AutoUpdateTask_1.default(),
16
18
  new SyncDepositEvents_1.default({
17
19
  chainId: 43114
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getContract = exports.buildWithdrawDataForTransaction = exports.buildDepositDataForTransaction = exports.buildDataForTransaction = exports.generateInteropTransactionHash = exports.asyncCallWithTimeout = exports.buildSignatureBytes = exports.getRpcProviderUrl = exports.signGnosisSafeTx = exports.short = exports.http = void 0;
6
+ exports.getContract = exports.buildWithdrawDataForTransaction = exports.buildDepositDataForTransaction = exports.buildDataForTransaction = exports.generateInteropTransactionHash = exports.asyncCallWithTimeout = exports.buildSignatureBytes = exports.getRpcProviderUrl = exports.signGnosisSafeTx = exports.short = exports.shortenHash = exports.http = void 0;
7
7
  /**
8
8
  * @module util
9
9
  */
@@ -16,6 +16,16 @@ const config_1 = __importDefault(require("@/config"));
16
16
  const abi_1 = __importDefault(require("@/abi"));
17
17
  exports.http = axios_1.default.create();
18
18
  (0, axios_retry_1.default)(exports.http, { retries: 3, retryDelay: axios_retry_1.default.exponentialDelay });
19
+ function shortenHash(hash, length = 4) {
20
+ if (!hash)
21
+ return;
22
+ if (hash.length < 12)
23
+ return hash;
24
+ const beginningChars = hash.startsWith("0x") ? length + 2 : length;
25
+ const shortened = hash.substr(0, beginningChars) + "…" + hash.substr(-length);
26
+ return shortened;
27
+ }
28
+ exports.shortenHash = shortenHash;
19
29
  function short(buffer) {
20
30
  return buffer.toString('hex').slice(0, 8) + '...';
21
31
  }
@@ -59,11 +69,11 @@ exports.signGnosisSafeTx = signGnosisSafeTx;
59
69
  const getRpcProviderUrl = (chainId) => {
60
70
  switch (chainId) {
61
71
  case 1:
62
- return 'https://rpc.instadapp.io/mainnet';
72
+ return 'https://rpc.ankr.com/eth';
63
73
  case 137:
64
- return 'https://rpc.instadapp.io/polygon';
74
+ return 'https://rpc.ankr.com/polygon';
65
75
  case 43114:
66
- return 'https://rpc.instadapp.io/avalanche';
76
+ return 'https://rpc.ankr.com/avalanche';
67
77
  default:
68
78
  throw new Error(`Unknown chainId: ${chainId}`);
69
79
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instadapp/interop-x",
3
- "version": "0.0.0-dev.b5a9a93",
3
+ "version": "0.0.0-dev.b70f25f",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "engines": {
package/src/api/index.ts CHANGED
@@ -27,7 +27,7 @@ export const startApiServer = async () => {
27
27
 
28
28
  logger.log(`RPC Server listening at http://${HOST}:${PORT}`)
29
29
  } catch (err) {
30
- logger.error(err)
30
+ logger.error(err.message)
31
31
  process.exit(1)
32
32
  }
33
33
  }
package/src/index.ts CHANGED
@@ -64,6 +64,7 @@ import { Tasks } from "@/tasks";
64
64
  import { startPeer, protocol, peerPool } from "@/net";
65
65
  import { startApiServer } from '@/api';
66
66
  import { Transaction } from './db';
67
+ import { shortenHash } from './utils';
67
68
 
68
69
  async function main() {
69
70
 
@@ -78,7 +79,12 @@ async function main() {
78
79
  protocol.on('TransactionStatus', async (payload) => {
79
80
  if (!peerPool.isLeadNode(payload.peerId)) {
80
81
  const peer = peerPool.getPeer(payload.peerId)
81
- logger.info(`ignored transaction status from ${payload.peerId} ${peer?.publicAddress} `)
82
+
83
+ if(! peer) {
84
+ return;
85
+ }
86
+
87
+ logger.info(`ignored transaction status from ${payload.peerId} ${shortenHash(peer.publicAddress)} `)
82
88
  return;
83
89
  }
84
90
 
@@ -17,6 +17,7 @@ import KadDHT from "libp2p-kad-dht";
17
17
  import PubsubPeerDiscovery from "libp2p-pubsub-peer-discovery";
18
18
  import { protocol } from "@/net";
19
19
  import config from "@/config";
20
+ import chalk from "chalk";
20
21
 
21
22
  const logger = new Logger("Peer");
22
23
 
@@ -80,7 +81,7 @@ export const startPeer = async ({ }: IPeerOptions) => {
80
81
  },
81
82
  });
82
83
 
83
- logger.info("Peer ID:", node.peerId.toB58String());
84
+ logger.info("Peer ID:", chalk.bold(node.peerId.toB58String()));
84
85
 
85
86
  await node.start();
86
87
 
@@ -2,6 +2,8 @@ import { Event } from "@/types";
2
2
  import config from "@/config";
3
3
  import Logger from "@/logger";
4
4
  import { getAddress } from "ethers/lib/utils";
5
+ import { shortenHash } from "@/utils";
6
+ import chalk from "chalk";
5
7
 
6
8
 
7
9
  const logger = new Logger('PeerPool')
@@ -87,7 +89,7 @@ export class PeerPool {
87
89
 
88
90
  if (newPeer) {
89
91
  config.events.emit(Event.POOL_PEER_ADDED, peer)
90
- logger.info(`Peer ${peer.id} with address ${peer.publicAddress} added to pool`)
92
+ logger.info(`Peer ${chalk.bold(shortenHash(peer.id, 16))} with address ${chalk.bold(shortenHash(peer.publicAddress))} added to pool`)
91
93
  }
92
94
  }
93
95
  }
@@ -102,7 +104,7 @@ export class PeerPool {
102
104
  if (this.pool.delete(peer.id)) {
103
105
  peer.pooled = false
104
106
  config.events.emit(Event.POOL_PEER_REMOVED, peer)
105
- logger.info(`Peer ${peer.id} with address ${peer.publicAddress} removed from pool`)
107
+ logger.info(`Peer ${chalk.bold(shortenHash(peer.id, 16))} with address ${chalk.bold(shortenHash(peer.publicAddress))} removed from pool`)
106
108
  }
107
109
  }
108
110
  }
@@ -123,7 +125,6 @@ export class PeerPool {
123
125
  return this.activePeers.map((p) => p.id)
124
126
  }
125
127
 
126
-
127
128
  getPeer(id: string){
128
129
  return this.pool.get(id);
129
130
  }
@@ -138,6 +139,9 @@ export class PeerPool {
138
139
  return getAddress(peer.publicAddress) === getAddress(config.leadNodeAddress)
139
140
  }
140
141
 
142
+ getLeadPeer() {
143
+ return this.peers.find((p) => this.isLeadNode(p.id))
144
+ }
141
145
 
142
146
  cleanup() {
143
147
  // let compDate = Date.now() - this.PEERS_CLEANUP_TIME_LIMIT * 60
@@ -0,0 +1,31 @@
1
+ import { BaseDialProtocol } from "./BaseDialProtocol";
2
+ import { Transaction } from "@/db";
3
+
4
+ export class TransactionStatusDialProtocol extends BaseDialProtocol<string, Pick<Transaction, 'transactionHash' | 'sourceStatus' | 'sourceTransactionHash' | 'sourceErrors' | 'targetStatus' | 'targetTransactionHash' | 'targetErrors' | 'status'> | null> {
5
+ protected timeout = 30000;
6
+
7
+ constructor(libp2p) {
8
+ super(libp2p, '/interop-x/transaction-status')
9
+ }
10
+
11
+ async response(transactionHash: string){
12
+ const transaction = await Transaction.findOne({ where: { transactionHash } })
13
+
14
+ if(! transaction){
15
+ return null
16
+ }
17
+ return {
18
+ transactionHash: transaction.transactionHash,
19
+
20
+ sourceStatus: transaction.sourceStatus,
21
+ sourceTransactionHash: transaction.sourceTransactionHash,
22
+ sourceErrors: transaction.sourceErrors,
23
+
24
+ targetStatus: transaction.targetStatus,
25
+ targetTransactionHash: transaction.targetTransactionHash,
26
+ targetErrors: transaction.targetErrors,
27
+
28
+ status: transaction.status,
29
+ }
30
+ }
31
+ }
@@ -6,6 +6,7 @@ import { IPeerInfo, peerPool } from "..";
6
6
  import config from "@/config";
7
7
  import { Event } from "@/types";
8
8
  import { Transaction } from "@/db";
9
+ import { TransactionStatusDialProtocol } from "./dial/SignatureDialProtocol.1";
9
10
 
10
11
  export interface ProtocolOptions {
11
12
  /* Handshake timeout in ms (default: 8000) */
@@ -90,6 +91,7 @@ class Protocol extends EventEmitter {
90
91
  },
91
92
  ];
92
93
  private signature: SignatureDialProtocol;
94
+ private transactionStatus: TransactionStatusDialProtocol;
93
95
 
94
96
 
95
97
  start({ libp2p, topic = null, }) {
@@ -109,6 +111,7 @@ class Protocol extends EventEmitter {
109
111
  })
110
112
 
111
113
  this.signature = new SignatureDialProtocol(this.libp2p);
114
+ this.transactionStatus = new TransactionStatusDialProtocol(this.libp2p);
112
115
  }
113
116
 
114
117
 
@@ -177,6 +180,15 @@ class Protocol extends EventEmitter {
177
180
  return []
178
181
  }
179
182
  }
183
+
184
+ async requestTransactionStatus(transactionHash: string, peerId: string) {
185
+ try {
186
+ return await this.transactionStatus.send(transactionHash, peerId);
187
+ } catch (error) {
188
+ console.log(error);
189
+ return null
190
+ }
191
+ }
180
192
  }
181
193
 
182
194
  export const protocol = new Protocol();
@@ -4,6 +4,10 @@ import { http } from "@/utils";
4
4
  import spawn from 'await-spawn';
5
5
  import config from "@/config";
6
6
  import wait from "waait";
7
+ import packageJson from "../../package.json";
8
+
9
+ const currentVersion = packageJson.version;
10
+ const packageName = packageJson.name;
7
11
 
8
12
  class AutoUpdateTask extends BaseTask {
9
13
  pollIntervalMs: number = 60 * 5 * 1000
@@ -18,15 +22,11 @@ class AutoUpdateTask extends BaseTask {
18
22
  return config.autoUpdate && !config.isLeadNode();
19
23
  }
20
24
 
21
- getCurrentVersion() {
22
- return require('../../package.json').version
23
- }
24
25
  async pollHandler() {
25
26
 
26
27
  const { data } = await http.get('https://registry.npmjs.org/@instadapp/interop-x')
27
28
 
28
29
  const version = data['dist-tags'].latest
29
- const currentVersion = this.getCurrentVersion()
30
30
 
31
31
  if (version === currentVersion) {
32
32
  return;
@@ -35,15 +35,16 @@ class AutoUpdateTask extends BaseTask {
35
35
  this.logger.warn(`New version ${version} available.`)
36
36
 
37
37
 
38
- await spawn('npm', ['-g', 'install', '@instadapp/interop-x', '-f']);
38
+ this.logger.info('Updating...')
39
39
 
40
+ await spawn('npm', ['-g', 'install', '@instadapp/interop-x', '-f']);
40
41
 
41
42
  await wait(5000)
42
43
 
43
- if (currentVersion === this.getCurrentVersion()) {
44
- this.logger.warn(`failed to install ${version}, retrying in 5 minutes`)
45
- return;
46
- }
44
+ // if (currentVersion === getCurrentVersion()) {
45
+ // this.logger.warn(`failed to install ${version}, retrying in 5 minutes`)
46
+ // return;
47
+ // }
47
48
 
48
49
  this.logger.warn(`Installed version ${version}`)
49
50
  this.logger.warn(`Restarting...`)
@@ -19,6 +19,7 @@ export class BaseTask extends EventEmitter implements IBaseTask {
19
19
  started: boolean = false
20
20
  pollIntervalMs: number = 10 * 1000
21
21
  leadNodeOnly: boolean = false
22
+ exceptLeadNode: boolean = false
22
23
 
23
24
  public constructor({ logger }: { logger?: Logger }) {
24
25
  super()
@@ -45,11 +46,15 @@ export class BaseTask extends EventEmitter implements IBaseTask {
45
46
  }
46
47
 
47
48
  prePollHandler(): boolean {
48
- if (!this.leadNodeOnly) {
49
- return true
49
+ if (this.exceptLeadNode) {
50
+ return !config.isLeadNode();
50
51
  }
51
52
 
52
- return config.isLeadNode()
53
+ if (this.leadNodeOnly) {
54
+ return config.isLeadNode()
55
+ }
56
+
57
+ return true
53
58
  }
54
59
 
55
60
  async pollHandler() {
@@ -220,8 +220,6 @@ class ProcessWithdrawEvents extends BaseTask {
220
220
  }
221
221
 
222
222
  async start(): Promise<void> {
223
- this.logger.info(`Starting execution watcher on interop chain`);
224
-
225
223
  this.provider = new ethers.providers.JsonRpcProvider(
226
224
  getRpcProviderUrl(this.chainId)
227
225
  );
@@ -102,8 +102,6 @@ class SyncWithdrawEvents extends BaseTask {
102
102
  }
103
103
 
104
104
  async start(): Promise<void> {
105
- this.logger.info(`Starting execution watcher on interop chain`);
106
-
107
105
  this.provider = new ethers.providers.JsonRpcProvider(
108
106
  getRpcProviderUrl(this.chainId)
109
107
  );
@@ -224,8 +224,6 @@ class ProcessDepositEvents extends BaseTask {
224
224
  }
225
225
 
226
226
  async start(): Promise<void> {
227
- this.logger.info(`Starting execution watcher on interop chain`);
228
-
229
227
  this.contractAddress = addresses[this.chainId].interopXGateway;
230
228
 
231
229
  this.provider = new ethers.providers.JsonRpcProvider(
@@ -105,8 +105,6 @@ class SyncDepositEvents extends BaseTask {
105
105
  }
106
106
 
107
107
  async start(): Promise<void> {
108
- this.logger.info(`Starting execution watcher on interop chain`);
109
-
110
108
  this.contractAddress = addresses[this.chainId].interopXGateway;
111
109
 
112
110
  this.provider = new ethers.providers.JsonRpcProvider(
@@ -0,0 +1,65 @@
1
+ import { BaseTask } from "../BaseTask";
2
+ import Logger from '@/logger';
3
+ import config from "@/config";
4
+ import { peerPool, protocol } from "@/net";
5
+ import { Transaction } from "@/db";
6
+ import { Op } from "sequelize";
7
+
8
+ class SyncTransactionStatusTask extends BaseTask {
9
+ pollIntervalMs: number = 60 * 1000
10
+ exceptLeadNode: boolean = true;
11
+
12
+ constructor() {
13
+ super({
14
+ logger: new Logger("SyncTransactionStatusTask"),
15
+ })
16
+ }
17
+
18
+ async pollHandler() {
19
+ // if transaction is pending for more than 1 hour, check lead node for status
20
+ const leadNode = peerPool.getLeadPeer();
21
+
22
+ if (!leadNode) {
23
+ return;
24
+ }
25
+
26
+ const transaction = await Transaction.findOne({
27
+ where: {
28
+ status: 'pending',
29
+ sourceCreatedAt: {
30
+ [Op.gte]: new Date(Date.now() - 60 * 60 * 1000),
31
+ },
32
+ }
33
+ })
34
+
35
+ if (!transaction) {
36
+ return;
37
+ }
38
+
39
+ this.logger.info(`Requesting transaction status for ${transaction.transactionHash}`)
40
+
41
+ const transactionStatus = await protocol.requestTransactionStatus(transaction.transactionHash, leadNode.id);
42
+
43
+ if (!transactionStatus) {
44
+ return;
45
+ }
46
+
47
+ this.logger.info(`Received transaction status for ${transaction.transactionHash}`)
48
+
49
+ transaction.sourceStatus = transactionStatus.sourceStatus
50
+ transaction.sourceTransactionHash = transactionStatus.sourceTransactionHash
51
+ transaction.sourceErrors = transactionStatus.sourceErrors
52
+
53
+ transaction.targetStatus = transactionStatus.targetStatus
54
+ transaction.targetTransactionHash = transactionStatus.targetTransactionHash
55
+ transaction.targetErrors = transactionStatus.targetErrors
56
+
57
+ transaction.status = transactionStatus.status
58
+
59
+ await transaction.save()
60
+
61
+ this.logger.info(`Updated transaction status for ${transaction.transactionHash}`)
62
+ }
63
+ }
64
+
65
+ export default SyncTransactionStatusTask;
@@ -5,10 +5,12 @@ import InteropXGatewaySyncDepositEvents from "./InteropXGateway/SyncDepositEvent
5
5
  import InteropBridgeSyncWithdrawEvents from "./InteropBridge/SyncWithdrawEvents";
6
6
  import InteropBridgeProcessWithdrawEvents from "./InteropBridge/ProcessWithdrawEvents";
7
7
  import AutoUpdateTask from "./AutoUpdateTask";
8
+ import SyncTransactionStatusTask from "./Transactions/SyncTransactionStatusTask";
8
9
 
9
10
  export class Tasks {
10
11
 
11
12
  tasks: BaseTask[] = [
13
+ new SyncTransactionStatusTask(),
12
14
  new AutoUpdateTask(),
13
15
 
14
16
  new InteropXGatewaySyncDepositEvents({
@@ -17,6 +17,18 @@ export const http = axios.create();
17
17
  axiosRetry(http, { retries: 3, retryDelay: axiosRetry.exponentialDelay });
18
18
 
19
19
 
20
+ export function shortenHash(hash: string, length: number = 4) {
21
+ if (!hash) return;
22
+
23
+ if (hash.length < 12) return hash;
24
+
25
+ const beginningChars = hash.startsWith("0x") ? length + 2 : length;
26
+
27
+ const shortened = hash.substr(0, beginningChars) + "…" + hash.substr(-length);
28
+
29
+ return shortened;
30
+ }
31
+
20
32
  export function short(buffer: Buffer): string {
21
33
  return buffer.toString('hex').slice(0, 8) + '...'
22
34
  }
@@ -76,11 +88,11 @@ export const signGnosisSafeTx = async ({
76
88
  export const getRpcProviderUrl = (chainId: ChainId) => {
77
89
  switch (chainId) {
78
90
  case 1:
79
- return 'https://rpc.instadapp.io/mainnet';
91
+ return 'https://rpc.ankr.com/eth';
80
92
  case 137:
81
- return 'https://rpc.instadapp.io/polygon';
93
+ return 'https://rpc.ankr.com/polygon';
82
94
  case 43114:
83
- return 'https://rpc.instadapp.io/avalanche';
95
+ return 'https://rpc.ankr.com/avalanche';
84
96
  default:
85
97
  throw new Error(`Unknown chainId: ${chainId}`);
86
98
  }