@instadapp/interop-x 0.0.0-dev.7a02577 → 0.0.0-dev.7adf1b5

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 (65) hide show
  1. package/dist/package.json +9 -5
  2. package/dist/src/abi/interopBridgeToken.json +21 -9
  3. package/dist/src/abi/interopXGateway.json +11 -11
  4. package/dist/src/api/index.js +6 -3
  5. package/dist/src/config/index.js +11 -1
  6. package/dist/src/constants/addresses.js +1 -1
  7. package/dist/src/constants/itokens.js +1 -1
  8. package/dist/src/db/models/transaction.js +8 -0
  9. package/dist/src/gnosis/actions/deposit.js +48 -0
  10. package/dist/src/gnosis/actions/index.js +11 -0
  11. package/dist/src/gnosis/actions/withdraw.js +50 -0
  12. package/dist/src/gnosis/index.js +20 -0
  13. package/dist/src/index.js +69 -7
  14. package/dist/src/net/peer/index.js +8 -3
  15. package/dist/src/net/pool/index.js +32 -9
  16. package/dist/src/net/protocol/dial/SignatureDialProtocol.js +6 -4
  17. package/dist/src/net/protocol/dial/TransactionStatusDialProtocol.js +28 -0
  18. package/dist/src/net/protocol/index.js +41 -1
  19. package/dist/src/tasks/AutoUpdateTask.js +70 -0
  20. package/dist/src/tasks/BaseTask.js +12 -4
  21. package/dist/src/tasks/InteropBridge/ProcessWithdrawEvents.js +162 -0
  22. package/dist/src/tasks/InteropBridge/SyncBurnEvents.js +71 -0
  23. package/dist/src/tasks/InteropBridge/SyncMintEvents.js +67 -0
  24. package/dist/src/tasks/InteropXGateway/ProcessDepositEvents.js +47 -23
  25. package/dist/src/tasks/InteropXGateway/SyncDepositEvents.js +6 -7
  26. package/dist/src/tasks/InteropXGateway/SyncWithdrawtEvents.js +72 -0
  27. package/dist/src/tasks/Transactions/SyncTransactionStatusTask.js +53 -0
  28. package/dist/src/tasks/index.js +28 -0
  29. package/dist/src/typechain/factories/InteropBridgeToken__factory.js +23 -11
  30. package/dist/src/typechain/factories/InteropXGateway__factory.js +14 -14
  31. package/dist/src/utils/index.js +57 -37
  32. package/package.json +9 -5
  33. package/patches/@ethersproject+properties+5.6.0.patch +13 -0
  34. package/src/abi/interopBridgeToken.json +21 -9
  35. package/src/abi/interopXGateway.json +11 -11
  36. package/src/api/index.ts +5 -2
  37. package/src/config/index.ts +11 -1
  38. package/src/constants/addresses.ts +1 -1
  39. package/src/constants/itokens.ts +1 -1
  40. package/src/db/models/transaction.ts +10 -0
  41. package/src/gnosis/actions/deposit.ts +63 -0
  42. package/src/gnosis/actions/index.ts +7 -0
  43. package/src/gnosis/actions/withdraw.ts +67 -0
  44. package/src/gnosis/index.ts +19 -0
  45. package/src/index.ts +90 -9
  46. package/src/net/peer/index.ts +9 -7
  47. package/src/net/pool/index.ts +41 -11
  48. package/src/net/protocol/dial/SignatureDialProtocol.ts +8 -6
  49. package/src/net/protocol/dial/TransactionStatusDialProtocol.ts +31 -0
  50. package/src/net/protocol/index.ts +57 -1
  51. package/src/tasks/AutoUpdateTask.ts +82 -0
  52. package/src/tasks/BaseTask.ts +14 -4
  53. package/src/tasks/InteropBridge/ProcessWithdrawEvents.ts +249 -0
  54. package/src/tasks/InteropBridge/SyncBurnEvents.ts +119 -0
  55. package/src/tasks/InteropBridge/SyncMintEvents.ts +99 -0
  56. package/src/tasks/InteropXGateway/ProcessDepositEvents.ts +60 -32
  57. package/src/tasks/InteropXGateway/SyncDepositEvents.ts +8 -10
  58. package/src/tasks/InteropXGateway/SyncWithdrawtEvents.ts +105 -0
  59. package/src/tasks/Transactions/SyncTransactionStatusTask.ts +65 -0
  60. package/src/tasks/index.ts +43 -2
  61. package/src/typechain/InteropBridgeToken.ts +23 -17
  62. package/src/typechain/InteropXGateway.ts +13 -13
  63. package/src/typechain/factories/InteropBridgeToken__factory.ts +23 -11
  64. package/src/typechain/factories/InteropXGateway__factory.ts +14 -14
  65. package/src/utils/index.ts +66 -53
@@ -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
 
@@ -2,12 +2,13 @@ import { BaseDialProtocol } from "./BaseDialProtocol";
2
2
  import wait from "waait";
3
3
  import config from "@/config";
4
4
  import { Transaction } from "@/db";
5
- import { buildDataForTransaction, signGnosisSafeTx } from "@/utils";
5
+ import { signGnosisSafeTx } from "@/utils";
6
6
  import { addresses } from "@/constants";
7
7
  import { ChainId } from "@/types";
8
+ import { buildGnosisAction } from "@/gnosis";
8
9
 
9
10
  export interface ISignatureRequest {
10
- type: 'source' | 'target' ,
11
+ type: 'source' | 'target',
11
12
  transactionHash: string
12
13
  safeTxGas: string
13
14
  safeNonce: string
@@ -46,13 +47,14 @@ export class SignatureDialProtocol extends BaseDialProtocol<ISignatureRequest, I
46
47
  error: 'Event not found'
47
48
  };
48
49
  }
50
+ const { data: gnosisData } = await buildGnosisAction(transaction, data.type);
49
51
 
50
52
  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,
53
+ to: addresses[transaction.targetChainId].multisend,
54
+ data: gnosisData,
55
+ chainId: transaction.targetChainId as ChainId,
55
56
  safeTxGas: data.safeTxGas,
57
+ nonce: data.safeNonce,
56
58
  }, { signer });
57
59
 
58
60
  return {
@@ -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
+ }
@@ -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' | '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() || null,
83
+ sourceErrors: sourceErrors.map((e) => e.toString()),
84
+
85
+ targetStatus: targetStatus.toString(),
86
+ targetTransactionHash: targetTransactionHash.toString()|| null,
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,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,249 @@
1
+ import { BaseTask } from "../BaseTask";
2
+ import Logger from '@/logger';
3
+ import { BigNumber, ethers } from "ethers";
4
+ import abi from "@/abi";
5
+ import { Transaction } from "@/db";
6
+ import { buildSignatureBytes, getContract, getRpcProviderUrl, Signature } from "@/utils";
7
+ import { addresses } from "@/constants";
8
+ import { ChainId } from "@/types";
9
+ import config from "@/config";
10
+ import { GnosisSafe } from "@/typechain";
11
+ import { Op } from "sequelize";
12
+ import wait from "waait";
13
+ import { peerPool, protocol } from "@/net";
14
+ import { LogDescription } from "ethers/lib/utils";
15
+ import { buildGnosisAction } from "@/gnosis";
16
+
17
+ const generateGnosisTransaction = async (transactionData: any, safeContract: GnosisSafe) => {
18
+ console.log(transactionData);
19
+
20
+ let isExecuted = await safeContract.dataHashes(
21
+ await safeContract.getTransactionHash(
22
+ transactionData.to,
23
+ transactionData.value,
24
+ transactionData.data,
25
+ transactionData.operation,
26
+ transactionData.safeTxGas,
27
+ transactionData.baseGas,
28
+ transactionData.gasPrice,
29
+ transactionData.gasToken,
30
+ transactionData.refundReceiver,
31
+ transactionData.nonce
32
+ )
33
+ )
34
+
35
+ while (isExecuted == 1) {
36
+ transactionData.safeTxGas = BigNumber.from(String(transactionData.safeTxGas)).add(1).toString()
37
+
38
+ isExecuted = await safeContract.dataHashes(
39
+ await safeContract.getTransactionHash(
40
+ transactionData.to,
41
+ transactionData.value,
42
+ transactionData.data,
43
+ transactionData.operation,
44
+ transactionData.safeTxGas,
45
+ transactionData.baseGas,
46
+ transactionData.gasPrice,
47
+ transactionData.gasToken,
48
+ transactionData.refundReceiver,
49
+ transactionData.nonce
50
+ )
51
+ )
52
+ }
53
+
54
+ return transactionData
55
+ }
56
+
57
+ class ProcessWithdrawEvents extends BaseTask {
58
+ provider: ethers.providers.JsonRpcProvider;
59
+ chainId: ChainId;
60
+ leadNodeOnly = true
61
+
62
+ constructor({ chainId }: { chainId: ChainId }) {
63
+ super({
64
+ logger: new Logger("InteropXGateway::ProcessWithdrawEvents"),
65
+ })
66
+ this.chainId = chainId;
67
+ }
68
+
69
+ async pollHandler() {
70
+ const blockNumber = await this.provider.getBlockNumber()
71
+
72
+ const transaction = await Transaction.findOne({
73
+ where: {
74
+ status: 'pending',
75
+ sourceStatus: 'success',
76
+ targetStatus: 'uninitialised',
77
+ action: 'withdraw',
78
+ sourceCreatedAt: {
79
+ [Op.gte]: new Date(Date.now() - 12 * 60 * 60 * 1000),
80
+ },
81
+ targetDelayUntil: {
82
+ [Op.or]: {
83
+ [Op.is]: null,
84
+ [Op.lt]: new Date(),
85
+ }
86
+ },
87
+ sourceBlockNumber: {
88
+ [Op.lt]: blockNumber - 12,
89
+ },
90
+ sourceChainId: this.chainId,
91
+ }
92
+ })
93
+
94
+ if (!transaction) {
95
+ return;
96
+ }
97
+
98
+ console.log(`Processing transaction ${transaction.transactionHash}`);
99
+
100
+ transaction.targetStatus = 'pending';
101
+ await transaction.save();
102
+
103
+ // refresh event data?
104
+
105
+ const targetChainProvider = new ethers.providers.JsonRpcProvider(
106
+ getRpcProviderUrl(transaction.targetChainId as ChainId)
107
+ );
108
+
109
+ const targetWallet = new ethers.Wallet(config.privateKey!, targetChainProvider);
110
+
111
+ const safeAddress = addresses[transaction.targetChainId].gnosisSafe;
112
+
113
+
114
+ const safeContract = getContract<GnosisSafe>(
115
+ safeAddress,
116
+ abi.gnosisSafe,
117
+ targetWallet
118
+ )
119
+
120
+ const ownersThreshold = await safeContract.getThreshold();
121
+ await wait(10000);
122
+
123
+ let data, logs = [];
124
+
125
+ try {
126
+ ({ data, logs } = await buildGnosisAction(transaction));
127
+ } catch (error) {
128
+ console.log(error);
129
+ transaction.targetStatus = 'failed';
130
+ transaction.targetErrors = [error.message];
131
+ transaction.status = 'failed'
132
+ await transaction.save();
133
+ protocol.sendTransaction(transaction)
134
+ return;
135
+ }
136
+
137
+ let gnosisTx = await generateGnosisTransaction({
138
+ baseGas: "0",
139
+ data,
140
+ gasPrice: "0",
141
+ gasToken: "0x0000000000000000000000000000000000000000",
142
+ nonce: '0',
143
+ operation: "1",
144
+ refundReceiver: "0x0000000000000000000000000000000000000000",
145
+ safeAddress: safeAddress,
146
+ safeTxGas: "79668",
147
+ to: addresses[transaction.targetChainId].multisend,
148
+ value: "0",
149
+ }, safeContract);
150
+
151
+ const owners = await safeContract.getOwners().then(owners => owners.map(owner => owner.toLowerCase()));
152
+
153
+ const ownerPeerIds = peerPool.activePeers.filter(peer => owners.includes(peer.publicAddress.toLowerCase())).map(peer => peer.id)
154
+
155
+ console.log(`Collecting signatures for execution ${transaction.transactionHash}`)
156
+
157
+ console.log(ownerPeerIds);
158
+
159
+ const signatures = await protocol.requestSignatures({
160
+ type: 'source',
161
+ transactionHash: transaction.transactionHash,
162
+ safeTxGas: gnosisTx.safeTxGas,
163
+ safeNonce: gnosisTx.nonce
164
+ }, ownerPeerIds)
165
+
166
+
167
+ const validSignatures = signatures.filter(s => !!s.data && s.data !== '0x') as Signature[];
168
+
169
+ console.log({ signatures, validSignatures, ownersThreshold: ownersThreshold.toString() });
170
+
171
+ if (validSignatures.length === 0 || ownersThreshold.gt(validSignatures.length)) {
172
+ await transaction.save();
173
+ transaction.targetDelayUntil = new Date(Date.now() + 30 * 1000);
174
+ transaction.targetStatus = 'uninitialised'
175
+
176
+ await transaction.save();
177
+ const errorMessage = signatures.find(s => !!s.error)?.error;
178
+ throw new Error(`Not enough signatures` + (errorMessage ? `: ${errorMessage}` : ''));
179
+ }
180
+
181
+
182
+ console.log(`Executing transaction for execution ${transaction.transactionHash}`)
183
+
184
+ const { data: txData } = await safeContract.populateTransaction.execTransaction(
185
+ gnosisTx.to,
186
+ gnosisTx.value,
187
+ gnosisTx.data,
188
+ gnosisTx.operation,
189
+ gnosisTx.safeTxGas,
190
+ gnosisTx.baseGas,
191
+ gnosisTx.gasPrice,
192
+ gnosisTx.gasToken,
193
+ gnosisTx.refundReceiver,
194
+ buildSignatureBytes(validSignatures)
195
+ );
196
+
197
+ console.log({
198
+ from: targetWallet.address,
199
+ gasPrice: BigNumber.from(120 * 10 ** 9).toString(),
200
+ to: safeAddress,
201
+ data: txData,
202
+ })
203
+
204
+
205
+ const txSent = await targetWallet.sendTransaction({
206
+ from: targetWallet.address,
207
+ gasPrice: BigNumber.from(120 * 10 ** 9),
208
+ to: safeAddress,
209
+ data: txData,
210
+ })
211
+
212
+ const receipt = await txSent.wait();
213
+
214
+ const parsedLogs: LogDescription[] = [];
215
+
216
+ receipt.logs.forEach((log) => {
217
+ try {
218
+ parsedLogs.push(safeContract.interface.parseLog(log));
219
+ } catch (e) { }
220
+ });
221
+
222
+ if (parsedLogs.find(e => e.name === 'ExecutionSuccess')) {
223
+ console.log('ExecutionSuccess')
224
+ transaction.targetStatus = 'success'
225
+ transaction.targetTransactionHash = txSent.hash
226
+ transaction.targetLogs = logs
227
+ transaction.status = 'success'
228
+ await transaction.save();
229
+ } else {
230
+ console.log('ExecutionFailure')
231
+ transaction.targetStatus = 'failed'
232
+ transaction.targetTransactionHash = txSent.hash
233
+ transaction.status = 'failed'
234
+ await transaction.save();
235
+ }
236
+
237
+ protocol.sendTransaction(transaction)
238
+ }
239
+
240
+ async start(): Promise<void> {
241
+ this.provider = new ethers.providers.JsonRpcProvider(
242
+ getRpcProviderUrl(this.chainId)
243
+ );
244
+
245
+ await super.start()
246
+ }
247
+ }
248
+
249
+ export default ProcessWithdrawEvents;