@instadapp/interop-x 0.0.0-dev.59f2858 → 0.0.0-dev.59f84ce

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. package/bin/interop-x +1 -1
  2. package/dist/package.json +73 -0
  3. package/dist/{abi → src/abi}/erc20.json +0 -0
  4. package/dist/{abi → src/abi}/gnosisSafe.json +0 -0
  5. package/dist/{abi → src/abi}/index.js +2 -4
  6. package/dist/src/abi/interopXContract.json +391 -0
  7. package/dist/src/alias.js +10 -0
  8. package/dist/{api → src/api}/index.js +6 -3
  9. package/dist/{config → src/config}/index.js +11 -1
  10. package/dist/{constants → src/constants}/addresses.js +3 -11
  11. package/dist/{constants → src/constants}/index.js +0 -0
  12. package/dist/{constants → src/constants}/tokens.js +0 -0
  13. package/dist/{db → src/db}/index.js +0 -0
  14. package/dist/{db → src/db}/models/index.js +0 -0
  15. package/dist/{db → src/db}/models/transaction.js +27 -7
  16. package/dist/{db → src/db}/sequelize.js +0 -0
  17. package/dist/src/gnosis/actions/index.js +9 -0
  18. package/dist/src/gnosis/actions/withdraw/index.js +41 -0
  19. package/dist/src/gnosis/index.js +20 -0
  20. package/dist/src/index.js +117 -0
  21. package/dist/{logger → src/logger}/index.js +0 -0
  22. package/dist/{net → src/net}/index.js +0 -0
  23. package/dist/{net → src/net}/peer/index.js +8 -3
  24. package/dist/{net → src/net}/pool/index.js +32 -9
  25. package/dist/{net → src/net}/protocol/dial/BaseDialProtocol.js +0 -0
  26. package/dist/{net → src/net}/protocol/dial/SignatureDialProtocol.js +17 -12
  27. package/dist/src/net/protocol/dial/TransactionStatusDialProtocol.js +30 -0
  28. package/dist/{net → src/net}/protocol/index.js +51 -1
  29. package/dist/src/tasks/AutoUpdateTask.js +70 -0
  30. package/dist/{tasks → src/tasks}/BaseTask.js +12 -4
  31. package/dist/src/tasks/InteropXContract/ProcessBridgeRequestEvents.js +146 -0
  32. package/dist/src/tasks/InteropXContract/SyncBridgeRequestEvents.js +78 -0
  33. package/dist/src/tasks/Transactions/SyncTransactionStatusTask.js +55 -0
  34. package/dist/src/tasks/index.js +33 -0
  35. package/dist/{typechain → src/typechain}/Erc20.js +0 -0
  36. package/dist/{typechain → src/typechain}/GnosisSafe.js +0 -0
  37. package/dist/{typechain/InteropBridgeToken.js → src/typechain/InteropXContract.js} +0 -0
  38. package/dist/{typechain → src/typechain}/common.js +0 -0
  39. package/dist/{typechain → src/typechain}/factories/Erc20__factory.js +0 -0
  40. package/dist/{typechain → src/typechain}/factories/GnosisSafe__factory.js +0 -0
  41. package/dist/src/typechain/factories/InteropXContract__factory.js +526 -0
  42. package/dist/src/typechain/factories/index.js +12 -0
  43. package/dist/{typechain → src/typechain}/index.js +3 -5
  44. package/dist/{types.js → src/types.js} +0 -0
  45. package/dist/src/utils/index.js +168 -0
  46. package/package.json +10 -6
  47. package/patches/@ethersproject+properties+5.6.0.patch +13 -0
  48. package/src/abi/index.ts +2 -4
  49. package/src/abi/interopXContract.json +391 -0
  50. package/src/alias.ts +6 -0
  51. package/src/api/index.ts +5 -2
  52. package/src/config/index.ts +11 -1
  53. package/src/constants/addresses.ts +3 -11
  54. package/src/db/models/transaction.ts +61 -19
  55. package/src/gnosis/actions/index.ts +5 -0
  56. package/src/gnosis/actions/withdraw/index.ts +56 -0
  57. package/src/gnosis/index.ts +19 -0
  58. package/src/index.ts +95 -24
  59. package/src/net/peer/index.ts +9 -7
  60. package/src/net/pool/index.ts +41 -11
  61. package/src/net/protocol/dial/SignatureDialProtocol.ts +19 -13
  62. package/src/net/protocol/dial/TransactionStatusDialProtocol.ts +33 -0
  63. package/src/net/protocol/index.ts +67 -1
  64. package/src/tasks/AutoUpdateTask.ts +82 -0
  65. package/src/tasks/BaseTask.ts +14 -4
  66. package/src/tasks/InteropXContract/ProcessBridgeRequestEvents.ts +208 -0
  67. package/src/tasks/InteropXContract/SyncBridgeRequestEvents.ts +116 -0
  68. package/src/tasks/Transactions/SyncTransactionStatusTask.ts +67 -0
  69. package/src/tasks/index.ts +16 -4
  70. package/src/typechain/InteropXContract.ts +524 -0
  71. package/src/typechain/factories/InteropXContract__factory.ts +533 -0
  72. package/src/typechain/factories/index.ts +1 -2
  73. package/src/typechain/index.ts +2 -4
  74. package/src/utils/index.ts +118 -7
  75. package/tsconfig.json +7 -2
  76. package/dist/abi/interopBridgeToken.json +0 -286
  77. package/dist/abi/interopXGateway.json +0 -184
  78. package/dist/index.js +0 -66
  79. package/dist/tasks/InteropXGateway/SyncDepositEvents.js +0 -66
  80. package/dist/tasks/index.js +0 -27
  81. package/dist/typechain/InteropXGateway.js +0 -2
  82. package/dist/typechain/factories/InteropBridgeToken__factory.js +0 -459
  83. package/dist/typechain/factories/InteropXGateway__factory.js +0 -265
  84. package/dist/typechain/factories/index.js +0 -14
  85. package/dist/utils/index.js +0 -101
  86. package/src/abi/interopBridgeToken.json +0 -286
  87. package/src/abi/interopXGateway.json +0 -184
  88. package/src/tasks/InteropXGateway/SyncDepositEvents.ts +0 -111
  89. package/src/typechain/InteropBridgeToken.ts +0 -686
  90. package/src/typechain/InteropXGateway.ts +0 -407
  91. package/src/typechain/factories/InteropBridgeToken__factory.ts +0 -466
  92. package/src/typechain/factories/InteropXGateway__factory.ts +0 -272
@@ -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/TransactionStatusDialProtocol";
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' | 'sourceLogs' | 'targetStatus' | 'targetTransactionHash' | 'targetErrors' | 'targetLogs' | '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,49 @@ 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
+ transaction.sourceLogs ? transaction.sourceLogs.map((e) => [Buffer.from(e.type), Buffer.from(e.message)]) : [],
72
+
73
+ Buffer.from(transaction.targetStatus),
74
+ Buffer.from(transaction.targetTransactionHash || ''),
75
+ transaction.targetErrors ? transaction.targetErrors.map((e) => Buffer.from(e)) : [],
76
+ transaction.targetLogs ? transaction.targetLogs.map((e) => [Buffer.from(e.type), Buffer.from(e.message)]) : [],
77
+
78
+ Buffer.from(transaction.status),
79
+ ],
80
+ decode: ([transactionHash, sourceStatus, sourceTransactionHash, sourceErrors, sourceLogs, targetStatus, targetTransactionHash, targetErrors, targetLogs, status]: [Buffer, Buffer, Buffer, Buffer[],[Buffer,Buffer][], Buffer, Buffer, Buffer[],[Buffer,Buffer][], Buffer]) => ({
81
+ transactionHash: transactionHash.toString(),
82
+
83
+ sourceStatus: sourceStatus.toString(),
84
+ sourceTransactionHash: sourceTransactionHash.toString() || null,
85
+ sourceErrors: sourceErrors.map((e) => e.toString()),
86
+ sourceLogs: sourceLogs.map(e => ({
87
+ type: e[0].toString(),
88
+ message: e[1].toString(),
89
+ })),
90
+
91
+ targetStatus: targetStatus.toString(),
92
+ targetTransactionHash: targetTransactionHash.toString() || null,
93
+ targetErrors: targetErrors.map((e) => e.toString()),
94
+ targetLogs: targetLogs.map(e => ({
95
+ type: e[0].toString(),
96
+ message: e[1].toString(),
97
+ })),
98
+
99
+ status: status.toString(),
100
+ }),
101
+ },
55
102
  ];
56
103
  private signature: SignatureDialProtocol;
104
+ private transactionStatus: TransactionStatusDialProtocol;
57
105
 
58
106
 
59
107
  start({ libp2p, topic = null, }) {
@@ -73,6 +121,7 @@ class Protocol extends EventEmitter {
73
121
  })
74
122
 
75
123
  this.signature = new SignatureDialProtocol(this.libp2p);
124
+ this.transactionStatus = new TransactionStatusDialProtocol(this.libp2p);
76
125
  }
77
126
 
78
127
 
@@ -121,6 +170,14 @@ class Protocol extends EventEmitter {
121
170
  this.libp2p.pubsub.publish(this.topic, encoded)
122
171
  }
123
172
 
173
+ public sendTransaction(transaction: Transaction) {
174
+ const message = this.protocolMessages.find((m) => m.name === 'TransactionStatus')!
175
+
176
+ const encoded = rlp.encode([message.code, message.encode(transaction)]);
177
+
178
+ this.libp2p.pubsub.publish(this.topic, encoded)
179
+ }
180
+
124
181
  async requestSignatures(data: ISignatureRequest, peerIds?: string[]) {
125
182
  try {
126
183
  peerIds = peerIds || peerPool.activePeerIds;
@@ -133,6 +190,15 @@ class Protocol extends EventEmitter {
133
190
  return []
134
191
  }
135
192
  }
193
+
194
+ async requestTransactionStatus(transactionHash: string, peerId: string) {
195
+ try {
196
+ return await this.transactionStatus.send(transactionHash, peerId);
197
+ } catch (error) {
198
+ console.log(error);
199
+ return null
200
+ }
201
+ }
136
202
  }
137
203
 
138
204
  export const protocol = new Protocol();
@@ -0,0 +1,82 @@
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
+
69
+ // TODO: its restarting in the bg, but it should be in the fg
70
+ const subprocess = spawn(process.argv[0], process.argv.slice(1), {
71
+ cwd: process.cwd(),
72
+ stdio: "inherit",
73
+ // shell: process.env.SHELL,
74
+ });
75
+
76
+ subprocess.unref();
77
+
78
+ process.exit()
79
+ }
80
+ }
81
+
82
+ 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() {
@@ -0,0 +1,208 @@
1
+ import { BaseTask } from "../BaseTask";
2
+ import Logger from '@/logger';
3
+ import { ethers, Wallet } from "ethers";
4
+ import abi from "@/abi";
5
+ import { Transaction } from "@/db";
6
+ import { buildSignatureBytes, generateGnosisTransaction, generateInteropTransactionHash, getContract, getRpcProviderUrl, Signature } from "@/utils";
7
+ import { addresses } from "@/constants";
8
+ import { Op } from "sequelize";
9
+ import { ChainId } from "@/types";
10
+ import config from "@/config";
11
+ import { GnosisSafe, InteropXContract } from "@/typechain";
12
+ import wait from "waait";
13
+ import { buildGnosisAction } from "@/gnosis";
14
+ import { peerPool, protocol } from "@/net";
15
+ import { LogDescription } from "ethers/lib/utils";
16
+
17
+ class ProccessBridgeRequestEvents extends BaseTask {
18
+ contractAddress: string;
19
+ safeContractAddress: string;
20
+ provider: ethers.providers.JsonRpcProvider;
21
+ contract: InteropXContract;
22
+ safeContract: GnosisSafe;
23
+ chainId: ChainId;
24
+ sourceWallet: Wallet;
25
+
26
+ leadNodeOnly: boolean = true;
27
+
28
+ constructor({ chainId }: { chainId: ChainId }) {
29
+ super({
30
+ logger: new Logger("InteropXContract::ProccessBridgeRequestEvents"),
31
+ })
32
+ this.chainId = chainId;
33
+ }
34
+
35
+ async pollHandler() {
36
+ const blockNumber = await this.provider.getBlockNumber()
37
+
38
+ const transaction = await Transaction.findOne({
39
+ where: {
40
+ status: 'pending',
41
+ sourceStatus: 'uninitialised',
42
+ createdAt: {
43
+ [Op.gte]: new Date(Date.now() - 12 * 60 * 60 * 1000),
44
+ },
45
+ sourceDelayUntil: {
46
+ [Op.or]: {
47
+ [Op.is]: null,
48
+ [Op.lt]: new Date(),
49
+ }
50
+ },
51
+ requestBlockNumber: {
52
+ [Op.lt]: blockNumber - 12,
53
+ },
54
+ sourceChainId: this.chainId,
55
+ }
56
+ })
57
+
58
+ if (!transaction) {
59
+ return;
60
+ }
61
+
62
+ console.log(`Processing transaction ${transaction.transactionHash}`);
63
+
64
+ transaction.targetStatus = 'pending';
65
+ await transaction.save();
66
+
67
+ const ownersThreshold = await this.safeContract.getThreshold();
68
+ await wait(10000);
69
+
70
+
71
+ let data, logs = [];
72
+
73
+ try {
74
+ ({ data, logs } = await buildGnosisAction(transaction));
75
+
76
+ } catch (error) {
77
+ console.log(error);
78
+ transaction.sourceStatus = 'failed';
79
+ transaction.sourceErrors = [error.message];
80
+ transaction.targetStatus = 'failed';
81
+
82
+ transaction.status = 'failed'
83
+ await transaction.save();
84
+ protocol.sendTransaction(transaction)
85
+ return;
86
+ }
87
+
88
+ let gnosisTx = await generateGnosisTransaction({
89
+ baseGas: "0",
90
+ data,
91
+ gasPrice: "0",
92
+ gasToken: "0x0000000000000000000000000000000000000000",
93
+ nonce: '0',
94
+ operation: "1",
95
+ refundReceiver: "0x0000000000000000000000000000000000000000",
96
+ safeAddress: this.safeContractAddress,
97
+ safeTxGas: "79668",
98
+ to: addresses[transaction.sourceChainId].multisend,
99
+ value: "0",
100
+ }, this.safeContract);
101
+
102
+ const owners = await this.safeContract.getOwners().then(owners => owners.map(owner => owner.toLowerCase()));
103
+
104
+ const ownerPeerIds = peerPool.activePeers.filter(peer => owners.includes(peer.publicAddress.toLowerCase())).map(peer => peer.id)
105
+
106
+ console.log(`Collecting signatures for execution ${transaction.transactionHash}`)
107
+
108
+ console.log(ownerPeerIds);
109
+
110
+ const signatures = await protocol.requestSignatures({
111
+ type: 'source',
112
+ transactionHash: transaction.transactionHash,
113
+ safeTxGas: gnosisTx.safeTxGas,
114
+ safeNonce: gnosisTx.nonce
115
+ }, ownerPeerIds)
116
+
117
+
118
+ const validSignatures = signatures.filter(s => !!s.data && s.data !== '0x') as Signature[];
119
+
120
+ console.log({ signatures, validSignatures, ownersThreshold: ownersThreshold.toString() });
121
+
122
+ if (validSignatures.length === 0 || ownersThreshold.gt(validSignatures.length)) {
123
+ await transaction.save();
124
+ transaction.sourceDelayUntil = new Date(Date.now() + 30 * 1000);
125
+ transaction.sourceStatus = 'uninitialised'
126
+
127
+ await transaction.save();
128
+ const errorMessage = signatures.find(s => !!s.error)?.error;
129
+ throw new Error(`Not enough signatures` + (errorMessage ? `: ${errorMessage}` : ''));
130
+ }
131
+
132
+ console.log(`Executing transaction for execution ${transaction.transactionHash}`)
133
+
134
+ const { data: txData } = await this.safeContract.populateTransaction.execTransaction(
135
+ gnosisTx.to,
136
+ gnosisTx.value,
137
+ gnosisTx.data,
138
+ gnosisTx.operation,
139
+ gnosisTx.safeTxGas,
140
+ gnosisTx.baseGas,
141
+ gnosisTx.gasPrice,
142
+ gnosisTx.gasToken,
143
+ gnosisTx.refundReceiver,
144
+ buildSignatureBytes(validSignatures)
145
+ );
146
+
147
+ const txSent = await this.sourceWallet.sendTransaction({
148
+ from: this.sourceWallet.address,
149
+ gasPrice: ethers.BigNumber.from(120 * 10 ** 9),
150
+ to: this.safeContractAddress,
151
+ data: txData,
152
+ })
153
+
154
+ const receipt = await txSent.wait();
155
+
156
+ const parsedLogs: LogDescription[] = [];
157
+
158
+ receipt.logs.forEach((log) => {
159
+ try {
160
+ parsedLogs.push(this.safeContract.interface.parseLog(log));
161
+ } catch (e) { }
162
+ });
163
+
164
+ if (parsedLogs.find(e => e.name === 'ExecutionSuccess')) {
165
+ console.log('ExecutionSuccess')
166
+ transaction.targetStatus = 'success'
167
+ transaction.targetTransactionHash = txSent.hash
168
+ transaction.targetLogs = logs;
169
+ transaction.status = 'success'
170
+ await transaction.save();
171
+ } else {
172
+ console.log('ExecutionFailure')
173
+ transaction.targetStatus = 'failed'
174
+ transaction.targetTransactionHash = txSent.hash
175
+ transaction.status = 'failed'
176
+ await transaction.save();
177
+ }
178
+
179
+ protocol.sendTransaction(transaction)
180
+ }
181
+
182
+ async start(): Promise<void> {
183
+ this.contractAddress = addresses[this.chainId].interopXContract;
184
+
185
+ this.provider = new ethers.providers.JsonRpcProvider(
186
+ getRpcProviderUrl(this.chainId)
187
+ );
188
+
189
+ this.sourceWallet = new ethers.Wallet(config.privateKey!, this.provider);
190
+
191
+ this.contract = getContract<InteropXContract>(
192
+ this.contractAddress,
193
+ abi.interopXContract,
194
+ this.sourceWallet
195
+ );
196
+
197
+ this.safeContractAddress = addresses[this.chainId].gnosisSafe;
198
+ this.safeContract = getContract<GnosisSafe>(
199
+ this.safeContractAddress,
200
+ abi.gnosisSafe,
201
+ this.sourceWallet
202
+ );
203
+
204
+ await super.start()
205
+ }
206
+ }
207
+
208
+ export default ProccessBridgeRequestEvents;
@@ -0,0 +1,116 @@
1
+ import { BaseTask } from "../BaseTask";
2
+ import Logger from '@/logger';
3
+ import { ethers } from "ethers";
4
+ import abi from "@/abi";
5
+ import { Transaction } from "@/db";
6
+ import { generateInteropTransactionHash, getContract, getRpcProviderUrl } from "@/utils";
7
+ import { addresses } from "@/constants";
8
+ import { ChainId } from "@/types";
9
+ import config from "@/config";
10
+ import { InteropXContract } from "@/typechain";
11
+
12
+ class SyncBridgeRequestEvents extends BaseTask {
13
+ contractAddress: string;
14
+ provider: ethers.providers.JsonRpcProvider;
15
+ contract: InteropXContract;
16
+ chainId: ChainId;
17
+
18
+ constructor({ chainId }: { chainId: ChainId }) {
19
+ super({
20
+ logger: new Logger("InteropXContract::SyncBridgeRequestEvents"),
21
+ })
22
+ this.chainId = chainId;
23
+ }
24
+
25
+ async pollHandler() {
26
+ const currentBlock = await this.provider.getBlockNumber();
27
+
28
+ const events = await this.contract.queryFilter(
29
+ this.contract.filters.LogBridgeRequest(),
30
+ currentBlock - 2000,
31
+ currentBlock,
32
+ );
33
+
34
+ let processedEvents = 0;
35
+
36
+ for (const event of events) {
37
+
38
+ try {
39
+ if (!event.args) {
40
+ continue;
41
+ }
42
+
43
+ const { bridger, position, sourceChainId, targetChainId, metadata } = event.args;
44
+
45
+ const uniqueIdentifier = {
46
+ action: 'withdraw', //todo
47
+ bridger,
48
+ requestTransactionHash: event.transactionHash,
49
+ sourceChainId: sourceChainId.toNumber(),
50
+ targetChainId: targetChainId.toNumber(),
51
+ }
52
+
53
+ let transactionHash = generateInteropTransactionHash(uniqueIdentifier);
54
+
55
+ const transaction = await Transaction.findOne({ where: { transactionHash } });
56
+
57
+ if (transaction) {
58
+ return;
59
+ }
60
+
61
+ await Transaction.create({
62
+ transactionHash,
63
+ ...uniqueIdentifier,
64
+ position,
65
+ metadata,
66
+ requestBlockNumber: event.blockNumber,
67
+ requestEvent: {
68
+ bridger,
69
+ position: {
70
+ withdraw: position.withdraw.map((v) => ({
71
+ sourceToken: v.sourceToken,
72
+ targetToken: v.targetToken,
73
+ amount: v.amount.toString()
74
+ })),
75
+ supply: position.supply.map((v) => ({
76
+ sourceToken: v.sourceToken,
77
+ targetToken: v.targetToken,
78
+ amount: v.amount.toString()
79
+ })),
80
+ },
81
+ sourceChainId: sourceChainId.toNumber(),
82
+ targetChainId: targetChainId.toNumber(),
83
+ metadata,
84
+ }
85
+ })
86
+
87
+ this.logger.info(
88
+ `New bridge request received: ${transactionHash} `
89
+ );
90
+ } catch (error) {
91
+ this.logger.error(error);
92
+ }
93
+ }
94
+
95
+ if (processedEvents > 0)
96
+ this.logger.info(`${processedEvents} events processed`);
97
+ }
98
+
99
+ async start(): Promise<void> {
100
+ this.contractAddress = addresses[this.chainId].interopXContract;
101
+
102
+ this.provider = new ethers.providers.JsonRpcProvider(
103
+ getRpcProviderUrl(this.chainId)
104
+ );
105
+
106
+ this.contract = getContract<InteropXContract>(
107
+ this.contractAddress,
108
+ abi.interopXContract,
109
+ new ethers.Wallet(config.privateKey!, this.provider)
110
+ );
111
+
112
+ await super.start()
113
+ }
114
+ }
115
+
116
+ export default SyncBridgeRequestEvents;
@@ -0,0 +1,67 @@
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
+ transaction.sourceLogs = transactionStatus.sourceLogs
53
+
54
+ transaction.targetStatus = transactionStatus.targetStatus
55
+ transaction.targetTransactionHash = transactionStatus.targetTransactionHash
56
+ transaction.targetErrors = transactionStatus.targetErrors
57
+ transaction.targetLogs = transactionStatus.targetLogs
58
+
59
+ transaction.status = transactionStatus.status
60
+
61
+ await transaction.save()
62
+
63
+ this.logger.info(`Updated transaction status for ${transaction.transactionHash}`)
64
+ }
65
+ }
66
+
67
+ export default SyncTransactionStatusTask;
@@ -1,12 +1,24 @@
1
1
  import { BaseTask } from "./BaseTask";
2
- import SyncInteropXGatewayDepositEvents from "./InteropXGateway/SyncDepositEvents";
2
+ import SyncTransactionStatusTask from "./Transactions/SyncTransactionStatusTask";
3
+
4
+ import AutoUpdateTask from "./AutoUpdateTask";
5
+
6
+ import SyncBridgeRequestEvents from "./InteropXContract/SyncBridgeRequestEvents";
7
+ import ProccessBridgeRequestEvents from "./InteropXContract/ProcessBridgeRequestEvents";
3
8
 
4
9
  export class Tasks {
5
10
 
6
11
  tasks: BaseTask[] = [
7
- new SyncInteropXGatewayDepositEvents({
8
- chainId: 43114
9
- })
12
+ // new SyncTransactionStatusTask(),
13
+ new AutoUpdateTask(),
14
+
15
+ // InteropXContract
16
+
17
+ new SyncBridgeRequestEvents({ chainId: 137 }),
18
+ new SyncBridgeRequestEvents({ chainId: 43114 }),
19
+
20
+ new ProccessBridgeRequestEvents({ chainId: 137 }),
21
+ new ProccessBridgeRequestEvents({ chainId: 43114 }),
10
22
  ];
11
23
 
12
24
  async start() {