@instadapp/interop-x 0.0.0-dev.1abc1ca → 0.0.0-dev.234b8be

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.
Files changed (40) hide show
  1. package/bin/interop-x +1 -1
  2. package/dist/package.json +9 -5
  3. package/dist/src/api/index.js +3 -3
  4. package/dist/src/config/index.js +11 -1
  5. package/dist/src/constants/itokens.js +1 -1
  6. package/dist/src/index.js +57 -5
  7. package/dist/src/net/peer/index.js +8 -3
  8. package/dist/src/net/pool/index.js +32 -9
  9. package/dist/src/net/protocol/dial/SignatureDialProtocol.1.js +28 -0
  10. package/dist/src/net/protocol/dial/SignatureDialProtocol.js +11 -4
  11. package/dist/src/net/protocol/index.js +41 -1
  12. package/dist/src/tasks/AutoUpdateTask.js +69 -0
  13. package/dist/src/tasks/BaseTask.js +12 -4
  14. package/dist/src/tasks/InteropBridge/ProcessWithdrawEvents.js +146 -0
  15. package/dist/src/tasks/InteropBridge/SyncWithdrawEvents.js +69 -0
  16. package/dist/src/tasks/InteropXGateway/ProcessDepositEvents.js +32 -23
  17. package/dist/src/tasks/InteropXGateway/SyncDepositEvents.js +4 -5
  18. package/dist/src/tasks/Transactions/SyncTransactionStatusTask.js +53 -0
  19. package/dist/src/tasks/index.js +17 -0
  20. package/dist/src/utils/index.js +111 -10
  21. package/package.json +9 -5
  22. package/patches/@ethersproject+properties+5.6.0.patch +13 -0
  23. package/src/api/index.ts +2 -2
  24. package/src/config/index.ts +11 -1
  25. package/src/constants/itokens.ts +1 -1
  26. package/src/index.ts +73 -8
  27. package/src/net/peer/index.ts +9 -7
  28. package/src/net/pool/index.ts +41 -11
  29. package/src/net/protocol/dial/SignatureDialProtocol.1.ts +31 -0
  30. package/src/net/protocol/dial/SignatureDialProtocol.ts +12 -4
  31. package/src/net/protocol/index.ts +57 -1
  32. package/src/tasks/AutoUpdateTask.ts +80 -0
  33. package/src/tasks/BaseTask.ts +14 -4
  34. package/src/tasks/InteropBridge/ProcessWithdrawEvents.ts +231 -0
  35. package/src/tasks/InteropBridge/SyncWithdrawEvents.ts +119 -0
  36. package/src/tasks/InteropXGateway/ProcessDepositEvents.ts +44 -33
  37. package/src/tasks/InteropXGateway/SyncDepositEvents.ts +6 -8
  38. package/src/tasks/Transactions/SyncTransactionStatusTask.ts +65 -0
  39. package/src/tasks/index.ts +24 -2
  40. package/src/utils/index.ts +146 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instadapp/interop-x",
3
- "version": "0.0.0-dev.1abc1ca",
3
+ "version": "0.0.0-dev.234b8be",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "engines": {
@@ -12,7 +12,8 @@
12
12
  "build": "yarn generate-abi-types && export GIT_REF=$(git rev-parse --short HEAD) && rimraf ./dist && tsc -p tsconfig.json && replace-in-file '@GIT_SHORT_HASH@' $GIT_REF ./dist/**/*.js",
13
13
  "dev": "yarn generate-abi-types && NODE_ENV=development nodemon",
14
14
  "generate-abi-types": "typechain --target=ethers-v5 'src/abi/*.json' --out-dir 'src/typechain'",
15
- "prepublishOnly": "yarn build"
15
+ "prepublishOnly": "yarn build",
16
+ "postinstall": "patch-package"
16
17
  },
17
18
  "nodemonConfig": {
18
19
  "watch": [
@@ -23,9 +24,10 @@
23
24
  },
24
25
  "dependencies": {
25
26
  "@achingbrain/libp2p-gossipsub": "^0.12.2",
27
+ "@fastify/cors": "^7.0.0",
28
+ "await-spawn": "^4.0.2",
26
29
  "axios": "^0.27.1",
27
30
  "axios-retry": "^3.2.4",
28
- "bignumber.js": "^9.0.2",
29
31
  "chalk": "4.1.2",
30
32
  "dotenv": "^16.0.0",
31
33
  "ethereumjs-util": "^7.1.4",
@@ -33,7 +35,7 @@
33
35
  "ethers-multisend": "^2.1.1",
34
36
  "expand-home-dir": "^0.0.3",
35
37
  "fastify": "^3.28.0",
36
- "fastify-cors": "^6.0.3",
38
+ "fs-extra": "^10.1.0",
37
39
  "libp2p": "^0.36.2",
38
40
  "libp2p-bootstrap": "^0.14.0",
39
41
  "libp2p-kad-dht": "^0.28.6",
@@ -45,7 +47,9 @@
45
47
  "libp2p-websockets": "^0.16.2",
46
48
  "luxon": "^2.3.2",
47
49
  "module-alias": "^2.2.2",
48
- "sequelize": "^6.19.0",
50
+ "patch-package": "^6.4.7",
51
+ "postinstall-postinstall": "^2.1.0",
52
+ "sequelize": "6.18.0",
49
53
  "sqlite3": "^5.0.5",
50
54
  "waait": "^1.0.5"
51
55
  },
@@ -0,0 +1,13 @@
1
+ diff --git a/node_modules/@ethersproject/properties/lib/index.js b/node_modules/@ethersproject/properties/lib/index.js
2
+ index 41e0b52..4c7a9e3 100644
3
+ --- a/node_modules/@ethersproject/properties/lib/index.js
4
+ +++ b/node_modules/@ethersproject/properties/lib/index.js
5
+ @@ -44,7 +44,7 @@ function defineReadOnly(object, name, value) {
6
+ Object.defineProperty(object, name, {
7
+ enumerable: true,
8
+ value: value,
9
+ - writable: false,
10
+ + writable: true,
11
+ });
12
+ }
13
+ exports.defineReadOnly = defineReadOnly;
package/src/api/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import fastify from "fastify"
2
- import cors from 'fastify-cors'
2
+ import cors from '@fastify/cors'
3
3
  import Logger from "@/logger"
4
4
  import { Transaction } from "@/db";
5
5
 
@@ -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
  }
@@ -1,5 +1,7 @@
1
1
  import { ethers, Wallet } from "ethers"
2
2
  import { EventBus, EventBusType } from "@/types"
3
+ import fs from 'fs-extra'
4
+ import expandHomeDir from "expand-home-dir";
3
5
 
4
6
  class Config {
5
7
  public readonly events: EventBusType
@@ -8,14 +10,18 @@ class Config {
8
10
  public readonly privateKey: string
9
11
  public readonly wallet: Wallet
10
12
  public readonly staging: boolean
13
+ public readonly autoUpdate: boolean
14
+ public readonly baseConfigPath: string
11
15
 
12
16
  constructor() {
13
17
  this.events = new EventBus() as EventBusType
14
- this.maxPeers = 10
18
+ this.maxPeers = 20
15
19
  this.privateKey = process.env.PRIVATE_KEY as string;
16
20
  this.staging = !! process.env.STAGING && process.env.STAGING === 'true';
21
+ this.autoUpdate = !! process.env.AUTO_UPDATE && process.env.AUTO_UPDATE === 'true';
17
22
  this.wallet = new Wallet(this.privateKey);
18
23
  this.leadNodeAddress = '0x910E413DBF3F6276Fe8213fF656726bDc142E08E'
24
+ this.baseConfigPath = expandHomeDir(`~/.interop-x`);
19
25
  }
20
26
 
21
27
  get publicAddress(){
@@ -25,6 +31,10 @@ class Config {
25
31
  isLeadNode() {
26
32
  return ethers.utils.getAddress(this.leadNodeAddress) === ethers.utils.getAddress(this.wallet.address)
27
33
  }
34
+
35
+ isMaintenanceMode(){
36
+ return fs.existsSync(this.baseConfigPath + '/maintenance')
37
+ }
28
38
  }
29
39
 
30
40
  export default new Config()
@@ -2,7 +2,7 @@ export const itokens = {
2
2
  1: [],
3
3
  137: [
4
4
  {
5
- address: '0x6c20F03598d5ABF729348E2868b0ff5e8A48aB1F',
5
+ address: '0xEab02fe1F016eE3e4106c1C6aad35FeEe657268E',
6
6
  symbol: 'USDC',
7
7
  }
8
8
  ],
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import moduleAlias from 'module-alias';
2
-
2
+ import expandHomeDir from "expand-home-dir";
3
+ import fs from 'fs-extra'
3
4
  moduleAlias.addAliases({
4
5
  "@/": __dirname + "/",
5
6
  "@/logger": __dirname + "/logger",
@@ -16,8 +17,8 @@ moduleAlias.addAliases({
16
17
  })
17
18
 
18
19
  moduleAlias();
19
- import assert from "assert";
20
20
  import dotenv from "dotenv";
21
+ import chalk from 'chalk';
21
22
  import { ethers } from "ethers";
22
23
  import packageJson from '../package.json'
23
24
  dotenv.config();
@@ -25,28 +26,61 @@ dotenv.config();
25
26
  import Logger from "@/logger";
26
27
  const logger = new Logger('Process')
27
28
 
28
-
29
- if (process.argv.at(-1) === 'help') {
29
+ const printUsage = () => {
30
30
  console.log('Usage:')
31
31
  console.log(' PRIVATE_KEY=abcd1234 interop-x')
32
32
  console.log(' PRIVATE_KEY=abcd1234 STAGING=true interop-x')
33
+ console.log(' PRIVATE_KEY=abcd1234 AUTO_UPDATE=true interop-x')
34
+ console.log(' PRIVATE_KEY=abcd1234 API_HOST=0.0.0.0 API_PORT=8080 interop-x')
35
+ }
36
+
37
+ if (process.argv.at(-1) === 'help') {
38
+ printUsage()
39
+ process.exit(0)
40
+ }
41
+
42
+ const basePath = expandHomeDir(`~/.interop-x`);
43
+
44
+ if (process.argv.at(-1) === 'down') {
45
+ fs.outputFileSync(basePath + '/maintenance', Date.now().toString())
46
+ console.log(chalk.red('Maintenance mode enabled'))
33
47
  process.exit(0)
34
48
  }
35
49
 
36
- assert(process.env.PRIVATE_KEY, "PRIVATE_KEY is not defined");
50
+ if (process.argv.at(-1) === 'up') {
51
+ fs.removeSync(basePath + '/maintenance')
52
+ console.log(chalk.green('Maintenance mode disabled'))
53
+ process.exit(0)
54
+ }
55
+
56
+
57
+ const GIT_SHORT_HASH = '@GIT_SHORT_HASH@';
37
58
 
59
+ if (process.argv.at(-1) === 'version') {
60
+ console.log(`Interop X Node (v${packageJson.version} - rev.${GIT_SHORT_HASH})`)
61
+ process.exit(0)
62
+ }
63
+
64
+ if (!process.env.PRIVATE_KEY) {
65
+ console.error(chalk.bgRed.white.bold('Please provide a private key\n'))
66
+ printUsage()
67
+ process.exit(1)
68
+ }
38
69
  try {
39
70
  new ethers.Wallet(process.env.PRIVATE_KEY!)
40
71
  } catch (e) {
41
- logger.error('Invalid private key')
72
+ console.error(chalk.bgRed.white('Invalid private key\n'))
73
+ printUsage()
42
74
  process.exit(1)
43
75
  }
44
76
 
45
- logger.debug(`Starting Interop X Node (v${packageJson.version} - rev.@GIT_SHORT_HASH@)`)
77
+ logger.debug(`Starting Interop X Node (v${packageJson.version} - rev.${GIT_SHORT_HASH})`)
46
78
 
47
79
  import { Tasks } from "@/tasks";
48
- import { startPeer } from "@/net";
80
+ import { startPeer, protocol, peerPool } from "@/net";
49
81
  import { startApiServer } from '@/api';
82
+ import { Transaction } from './db';
83
+ import { shortenHash } from './utils';
50
84
 
51
85
  async function main() {
52
86
 
@@ -57,6 +91,37 @@ async function main() {
57
91
  tasks.start();
58
92
 
59
93
  startApiServer()
94
+
95
+ protocol.on('TransactionStatus', async (payload) => {
96
+ if (!peerPool.isLeadNode(payload.peerId)) {
97
+ const peer = peerPool.getPeer(payload.peerId)
98
+
99
+ if (!peer) {
100
+ return;
101
+ }
102
+
103
+ logger.info(`ignored transaction status from ${payload.peerId} ${shortenHash(peer.publicAddress)} `)
104
+ return;
105
+ }
106
+
107
+ const transaction = await Transaction.findOne({ where: { transactionHash: payload.data.transactionHash } })
108
+
109
+ if (!transaction) {
110
+ return;
111
+ }
112
+
113
+ transaction.sourceStatus = payload.data.sourceStatus
114
+ transaction.sourceTransactionHash = payload.data.sourceTransactionHash
115
+ transaction.sourceErrors = payload.data.sourceErrors
116
+
117
+ transaction.targetStatus = payload.data.targetStatus
118
+ transaction.targetTransactionHash = payload.data.targetTransactionHash
119
+ transaction.targetErrors = payload.data.targetErrors
120
+
121
+ transaction.status = payload.data.status
122
+
123
+ await transaction.save()
124
+ })
60
125
  }
61
126
 
62
127
  main()
@@ -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
 
@@ -88,12 +89,13 @@ export const startPeer = async ({ }: IPeerOptions) => {
88
89
  libp2p: node
89
90
  })
90
91
 
91
- node.on("peer:discovery", (peer) =>
92
- logger.log(`Discovered peer ${peer}`)
93
- ); // peer disc.
94
- node.connectionManager.on("peer:connect", (connection) =>
95
- logger.log(`Connected to ${connection.remotePeer.toB58String()}`)
96
- );
92
+ node.on("peer:discovery", (peer) => {
93
+ // logger.log(`Discovered peer ${peer}`)
94
+ }); // peer disc.
95
+
96
+ node.connectionManager.on("peer:connect", (connection) => {
97
+ // logger.log(`Connected to ${connection.remotePeer.toB58String()}`)
98
+ });
97
99
 
98
100
  logger.log("Peer discovery started");
99
101
 
@@ -1,5 +1,12 @@
1
1
  import { Event } from "@/types";
2
2
  import config from "@/config";
3
+ import Logger from "@/logger";
4
+ import { getAddress } from "ethers/lib/utils";
5
+ import { shortenHash } from "@/utils";
6
+ import chalk from "chalk";
7
+
8
+
9
+ const logger = new Logger('PeerPool')
3
10
 
4
11
  export interface IPeerInfo {
5
12
  id: string;
@@ -75,10 +82,15 @@ export class PeerPool {
75
82
  * @emits {@link Event.POOL_PEER_ADDED}
76
83
  */
77
84
  add(peer?: IPeerInfo) {
78
- if (peer && peer.id && !this.pool.get(peer.id)) {
85
+ if (peer && peer.id) {
86
+ const newPeer = !this.pool.get(peer.id);
79
87
  this.pool.set(peer.id, peer)
80
88
  peer.pooled = true
81
- config.events.emit(Event.POOL_PEER_ADDED, peer)
89
+
90
+ if (newPeer) {
91
+ config.events.emit(Event.POOL_PEER_ADDED, peer)
92
+ logger.info(`Peer ${chalk.bold(shortenHash(peer.id, 16))} with address ${chalk.bold(shortenHash(peer.publicAddress))} added to pool`)
93
+ }
82
94
  }
83
95
  }
84
96
 
@@ -92,6 +104,7 @@ export class PeerPool {
92
104
  if (this.pool.delete(peer.id)) {
93
105
  peer.pooled = false
94
106
  config.events.emit(Event.POOL_PEER_REMOVED, peer)
107
+ logger.info(`Peer ${chalk.bold(shortenHash(peer.id, 16))} with address ${chalk.bold(shortenHash(peer.publicAddress))} removed from pool`)
95
108
  }
96
109
  }
97
110
  }
@@ -100,7 +113,7 @@ export class PeerPool {
100
113
  this.cleanup()
101
114
 
102
115
  return this.peers.filter((p) => {
103
- if(!p.pooled) return false;
116
+ if (!p.pooled) return false;
104
117
 
105
118
  const now = new Date()
106
119
 
@@ -112,16 +125,33 @@ export class PeerPool {
112
125
  return this.activePeers.map((p) => p.id)
113
126
  }
114
127
 
128
+ getPeer(id: string){
129
+ return this.pool.get(id);
130
+ }
115
131
 
116
- cleanup() {
117
- let compDate = Date.now() - this.PEERS_CLEANUP_TIME_LIMIT * 60
132
+ isLeadNode(id: string) {
133
+ const peer = this.pool.get(id);
118
134
 
119
- this.peers.forEach((peerInfo) => {
120
- if (peerInfo.updated.getTime() < compDate) {
121
- console.log(`Peer ${peerInfo.id} idle for ${this.PEERS_CLEANUP_TIME_LIMIT} minutes`)
122
- this.remove(peerInfo)
123
- }
124
- })
135
+ if (!peer) {
136
+ return false;
137
+ }
138
+
139
+ return getAddress(peer.publicAddress) === getAddress(config.leadNodeAddress)
140
+ }
141
+
142
+ getLeadPeer() {
143
+ return this.peers.find((p) => this.isLeadNode(p.id))
144
+ }
145
+
146
+ cleanup() {
147
+ // let compDate = Date.now() - this.PEERS_CLEANUP_TIME_LIMIT * 60
148
+
149
+ // this.peers.forEach((peerInfo) => {
150
+ // if (peerInfo.updated.getTime() < compDate) {
151
+ // console.log(`Peer ${peerInfo.id} idle for ${this.PEERS_CLEANUP_TIME_LIMIT} minutes`)
152
+ // this.remove(peerInfo)
153
+ // }
154
+ // })
125
155
  }
126
156
  }
127
157
 
@@ -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
+ }
@@ -47,12 +47,20 @@ export class SignatureDialProtocol extends BaseDialProtocol<ISignatureRequest, I
47
47
  };
48
48
  }
49
49
 
50
+ console.log("signing:", {
51
+ to: addresses[transaction.targetChainId].multisend,
52
+ data: await buildDataForTransaction(transaction, data.type),
53
+ chainId: transaction.targetChainId as ChainId,
54
+ safeTxGas: data.safeTxGas,
55
+ nonce: data.safeNonce,
56
+ });
57
+
50
58
  const signedData = await signGnosisSafeTx({
51
- //TODO: chain id depends on event type
52
- to: addresses[transaction.sourceChainId].multisend,
53
- data: buildDataForTransaction(transaction, data.type),
54
- chainId: transaction.sourceChainId as ChainId,
59
+ to: addresses[transaction.targetChainId].multisend,
60
+ data: await buildDataForTransaction(transaction, data.type),
61
+ chainId: transaction.targetChainId as ChainId,
55
62
  safeTxGas: data.safeTxGas,
63
+ nonce: data.safeNonce,
56
64
  }, { signer });
57
65
 
58
66
  return {
@@ -5,6 +5,8 @@ import { SignatureDialProtocol, ISignatureRequest, ISignatureResponse } from "./
5
5
  import { IPeerInfo, peerPool } from "..";
6
6
  import config from "@/config";
7
7
  import { Event } from "@/types";
8
+ import { Transaction } from "@/db";
9
+ import { TransactionStatusDialProtocol } from "./dial/SignatureDialProtocol.1";
8
10
 
9
11
  export interface ProtocolOptions {
10
12
  /* Handshake timeout in ms (default: 8000) */
@@ -33,7 +35,12 @@ interface PeerInfoEvent extends BaseMessageEvent {
33
35
  data: Omit<IPeerInfo, 'id' | 'updated' | 'idle' | 'pooled'>
34
36
  }
35
37
 
38
+ interface TransactionStatusEvent extends BaseMessageEvent {
39
+ data: Pick<Transaction, 'transactionHash' | 'sourceStatus' | 'sourceTransactionHash' | 'sourceErrors' | 'targetStatus' | 'targetTransactionHash' | 'targetErrors' | 'status'>
40
+ }
41
+
36
42
  declare interface Protocol {
43
+ on(event: 'TransactionStatus', listener: (payload: TransactionStatusEvent) => void): this;
37
44
  on(event: 'PeerInfo', listener: (payload: PeerInfoEvent) => void): this;
38
45
  on(event: string, listener: (payload: BaseMessageEvent) => void): this;
39
46
  }
@@ -44,7 +51,7 @@ class Protocol extends EventEmitter {
44
51
  private protocolMessages: Message[] = [
45
52
  {
46
53
  name: 'PeerInfo',
47
- code: 0x09,
54
+ code: 0x01,
48
55
  encode: (info: Pick<IPeerInfo, 'publicAddress'>) => [
49
56
  Buffer.from(info.publicAddress),
50
57
  ],
@@ -52,8 +59,39 @@ class Protocol extends EventEmitter {
52
59
  publicAddress: publicAddress.toString(),
53
60
  }),
54
61
  },
62
+ {
63
+ name: 'TransactionStatus',
64
+ code: 0x02,
65
+ encode: (transaction: Transaction) => [
66
+ Buffer.from(transaction.transactionHash),
67
+
68
+ Buffer.from(transaction.sourceStatus),
69
+ Buffer.from(transaction.sourceTransactionHash),
70
+ transaction.sourceErrors ? transaction.sourceErrors.map((e) => Buffer.from(e)) : [],
71
+
72
+ Buffer.from(transaction.targetStatus),
73
+ Buffer.from(transaction.targetTransactionHash),
74
+ transaction.targetErrors ? transaction.targetErrors.map((e) => Buffer.from(e)) : [],
75
+
76
+ Buffer.from(transaction.status),
77
+ ],
78
+ decode: ([transactionHash, sourceStatus, sourceTransactionHash, sourceErrors, targetStatus, targetTransactionHash, targetErrors, status]: [Buffer, Buffer, Buffer, Buffer[], Buffer, Buffer, Buffer[], Buffer]) => ({
79
+ transactionHash: transactionHash.toString(),
80
+
81
+ sourceStatus: sourceStatus.toString(),
82
+ sourceTransactionHash: sourceTransactionHash.toString(),
83
+ sourceErrors: sourceErrors.map((e) => e.toString()),
84
+
85
+ targetStatus: targetStatus.toString(),
86
+ targetTransactionHash: targetTransactionHash.toString(),
87
+ targetErrors: targetErrors.map((e) => e.toString()),
88
+
89
+ status: status.toString(),
90
+ }),
91
+ },
55
92
  ];
56
93
  private signature: SignatureDialProtocol;
94
+ private transactionStatus: TransactionStatusDialProtocol;
57
95
 
58
96
 
59
97
  start({ libp2p, topic = null, }) {
@@ -73,6 +111,7 @@ class Protocol extends EventEmitter {
73
111
  })
74
112
 
75
113
  this.signature = new SignatureDialProtocol(this.libp2p);
114
+ this.transactionStatus = new TransactionStatusDialProtocol(this.libp2p);
76
115
  }
77
116
 
78
117
 
@@ -121,6 +160,14 @@ class Protocol extends EventEmitter {
121
160
  this.libp2p.pubsub.publish(this.topic, encoded)
122
161
  }
123
162
 
163
+ public sendTransaction(transaction: Transaction) {
164
+ const message = this.protocolMessages.find((m) => m.name === 'TransactionStatus')!
165
+
166
+ const encoded = rlp.encode([message.code, message.encode(transaction)]);
167
+
168
+ this.libp2p.pubsub.publish(this.topic, encoded)
169
+ }
170
+
124
171
  async requestSignatures(data: ISignatureRequest, peerIds?: string[]) {
125
172
  try {
126
173
  peerIds = peerIds || peerPool.activePeerIds;
@@ -133,6 +180,15 @@ class Protocol extends EventEmitter {
133
180
  return []
134
181
  }
135
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
+ }
136
192
  }
137
193
 
138
194
  export const protocol = new Protocol();
@@ -0,0 +1,80 @@
1
+ import { BaseTask } from "./BaseTask";
2
+ import Logger from '@/logger';
3
+ import spawnAsync from 'await-spawn';
4
+ import { spawn } from 'child_process'
5
+ import config from "@/config";
6
+ import wait from "waait";
7
+ import packageJson from "../../package.json";
8
+
9
+ const currentVersion = packageJson.version;
10
+ const tag = config.staging ? 'dev' : 'latest';
11
+
12
+ class AutoUpdateTask extends BaseTask {
13
+ pollIntervalMs: number = 60 * 10 * 1000
14
+
15
+ constructor() {
16
+ super({
17
+ logger: new Logger("AutoUpdateTask"),
18
+ })
19
+ }
20
+
21
+ prePollHandler(): boolean {
22
+ return config.autoUpdate && !config.isLeadNode();
23
+ }
24
+
25
+ async getInstalledVersion() {
26
+ try {
27
+ const stdout = await spawnAsync('npm', ['-g', 'ls', '--depth=0', '--json'])
28
+ return JSON.parse(stdout.toString()).dependencies[packageJson.name].version
29
+ } catch (error) {
30
+ this.logger.error(error)
31
+ return currentVersion
32
+ }
33
+ }
34
+
35
+ async getLatestVersion() {
36
+ try {
37
+ const stdout = await spawnAsync('npm', ['view', `${packageJson.name}@${tag}`, 'version'])
38
+ return stdout.toString().trim()
39
+ } catch (error) {
40
+ this.logger.error(error)
41
+ return currentVersion
42
+ }
43
+ }
44
+
45
+ async pollHandler() {
46
+ const version = await this.getLatestVersion()
47
+
48
+ if (version === currentVersion) {
49
+ return;
50
+ }
51
+
52
+ this.logger.warn(`New version ${version} available.`)
53
+
54
+ this.logger.info('Updating...')
55
+
56
+ await spawnAsync('npm', ['-g', 'install', `@instadapp/interop-x@${tag}`, '-f']);
57
+
58
+ await wait(5000)
59
+
60
+ if (version !== await this.getInstalledVersion()) {
61
+ this.logger.warn(`failed to install ${version}, retrying in 5 minutes`)
62
+ return;
63
+ }
64
+
65
+ this.logger.warn(`Installed version ${version}`)
66
+ this.logger.warn(`Restarting...`)
67
+
68
+ const subprocess = spawn(process.argv[0], process.argv.slice(1), {
69
+ cwd: process.cwd(),
70
+ stdio: "inherit",
71
+ shell: process.env.SHELL,
72
+ });
73
+
74
+ subprocess.unref();
75
+
76
+ process.exit()
77
+ }
78
+ }
79
+
80
+ export default AutoUpdateTask;
@@ -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()
@@ -37,7 +38,7 @@ export class BaseTask extends EventEmitter implements IBaseTask {
37
38
  await this.pollHandler()
38
39
  }
39
40
  } catch (err) {
40
- this.logger.error(`poll check error: ${err.message}\ntrace: ${err.stack}`)
41
+ this.logger.error(`poll check error:\n${err.message}\ntrace: ${err.stack}`)
41
42
  }
42
43
 
43
44
  await this.postPollHandler()
@@ -45,11 +46,20 @@ export class BaseTask extends EventEmitter implements IBaseTask {
45
46
  }
46
47
 
47
48
  prePollHandler(): boolean {
48
- if (!this.leadNodeOnly) {
49
- return true
49
+ if(config.isMaintenanceMode()){
50
+ this.logger.warn('Maintenance mode is enabled. Skipping task.')
51
+ return false
50
52
  }
51
53
 
52
- return config.isLeadNode()
54
+ if (this.exceptLeadNode) {
55
+ return !config.isLeadNode();
56
+ }
57
+
58
+ if (this.leadNodeOnly) {
59
+ return config.isLeadNode()
60
+ }
61
+
62
+ return true
53
63
  }
54
64
 
55
65
  async pollHandler() {