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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. package/bin/interop-x +1 -1
  2. package/dist/package.json +8 -4
  3. package/dist/src/api/index.js +2 -2
  4. package/dist/src/config/index.js +1 -0
  5. package/dist/src/constants/addresses.js +0 -8
  6. package/dist/src/constants/index.js +1 -0
  7. package/dist/src/constants/itokens.js +13 -0
  8. package/dist/src/db/models/transaction.js +2 -0
  9. package/dist/src/index.js +40 -5
  10. package/dist/src/net/peer/index.js +6 -2
  11. package/dist/src/net/pool/index.js +27 -9
  12. package/dist/src/net/protocol/dial/SignatureDialProtocol.js +22 -12
  13. package/dist/src/net/protocol/index.js +30 -1
  14. package/dist/src/tasks/AutoUpdateTask.js +48 -0
  15. package/dist/src/tasks/BaseTask.js +1 -1
  16. package/dist/src/tasks/InteropBridge/ProcessWithdrawEvents.js +147 -0
  17. package/dist/src/tasks/InteropBridge/SyncWithdrawEvents.js +70 -0
  18. package/dist/src/tasks/InteropXGateway/ProcessDepositEvents.js +150 -0
  19. package/dist/src/tasks/InteropXGateway/SyncDepositEvents.js +14 -5
  20. package/dist/src/tasks/index.js +15 -0
  21. package/dist/src/utils/index.js +129 -2
  22. package/package.json +8 -4
  23. package/patches/@ethersproject+properties+5.6.0.patch +13 -0
  24. package/src/api/index.ts +1 -1
  25. package/src/config/index.ts +2 -0
  26. package/src/constants/addresses.ts +0 -8
  27. package/src/constants/index.ts +1 -0
  28. package/src/constants/itokens.ts +10 -0
  29. package/src/db/models/transaction.ts +6 -2
  30. package/src/index.ts +50 -7
  31. package/src/net/peer/index.ts +7 -6
  32. package/src/net/pool/index.ts +37 -11
  33. package/src/net/protocol/dial/SignatureDialProtocol.ts +25 -13
  34. package/src/net/protocol/index.ts +45 -1
  35. package/src/tasks/AutoUpdateTask.ts +60 -0
  36. package/src/tasks/BaseTask.ts +1 -1
  37. package/src/tasks/InteropBridge/ProcessWithdrawEvents.ts +233 -0
  38. package/src/tasks/InteropBridge/SyncWithdrawEvents.ts +121 -0
  39. package/src/tasks/InteropXGateway/ProcessDepositEvents.ts +245 -0
  40. package/src/tasks/InteropXGateway/SyncDepositEvents.ts +22 -7
  41. package/src/tasks/index.ts +22 -2
  42. package/src/utils/index.ts +181 -3
package/src/index.ts CHANGED
@@ -16,8 +16,8 @@ moduleAlias.addAliases({
16
16
  })
17
17
 
18
18
  moduleAlias();
19
- import assert from "assert";
20
19
  import dotenv from "dotenv";
20
+ import chalk from 'chalk';
21
21
  import { ethers } from "ethers";
22
22
  import packageJson from '../package.json'
23
23
  dotenv.config();
@@ -25,28 +25,45 @@ dotenv.config();
25
25
  import Logger from "@/logger";
26
26
  const logger = new Logger('Process')
27
27
 
28
-
29
- if (process.argv.at(-1) === 'help') {
28
+ const printUsage = () => {
30
29
  console.log('Usage:')
31
30
  console.log(' PRIVATE_KEY=abcd1234 interop-x')
32
31
  console.log(' PRIVATE_KEY=abcd1234 STAGING=true interop-x')
32
+ console.log(' PRIVATE_KEY=abcd1234 AUTO_UPDATE=true interop-x')
33
+ console.log(' PRIVATE_KEY=abcd1234 API_HOST=0.0.0.0 API_PORT=8080 interop-x')
34
+ }
35
+
36
+ if (process.argv.at(-1) === 'help') {
37
+ printUsage()
33
38
  process.exit(0)
34
39
  }
35
40
 
36
- assert(process.env.PRIVATE_KEY, "PRIVATE_KEY is not defined");
41
+ const GIT_SHORT_HASH = '@GIT_SHORT_HASH@';
37
42
 
43
+ if (process.argv.at(-1) === 'version') {
44
+ console.log(`Interop X Node (v${packageJson.version} - rev.${GIT_SHORT_HASH})`)
45
+ process.exit(0)
46
+ }
47
+
48
+ if(! process.env.PRIVATE_KEY) {
49
+ console.error(chalk.bgRed.white.bold('Please provide a private key\n'))
50
+ printUsage()
51
+ process.exit(1)
52
+ }
38
53
  try {
39
54
  new ethers.Wallet(process.env.PRIVATE_KEY!)
40
55
  } catch (e) {
41
- logger.error('Invalid private key')
56
+ console.error(chalk.bgRed.white('Invalid private key\n'))
57
+ printUsage()
42
58
  process.exit(1)
43
59
  }
44
60
 
45
- logger.debug(`Starting Interop X Node (v${packageJson.version} - rev.@GIT_SHORT_HASH@)`)
61
+ logger.debug(`Starting Interop X Node (v${packageJson.version} - rev.${GIT_SHORT_HASH})`)
46
62
 
47
63
  import { Tasks } from "@/tasks";
48
- import { startPeer } from "@/net";
64
+ import { startPeer, protocol, peerPool } from "@/net";
49
65
  import { startApiServer } from '@/api';
66
+ import { Transaction } from './db';
50
67
 
51
68
  async function main() {
52
69
 
@@ -57,6 +74,32 @@ async function main() {
57
74
  tasks.start();
58
75
 
59
76
  startApiServer()
77
+
78
+ protocol.on('TransactionStatus', async (payload) => {
79
+ if (!peerPool.isLeadNode(payload.peerId)) {
80
+ const peer = peerPool.getPeer(payload.peerId)
81
+ logger.info(`ignored transaction status from ${payload.peerId} ${peer?.publicAddress} `)
82
+ return;
83
+ }
84
+
85
+ const transaction = await Transaction.findOne({ where: { transactionHash: payload.data.transactionHash } })
86
+
87
+ if (!transaction) {
88
+ return;
89
+ }
90
+
91
+ transaction.sourceStatus = payload.data.sourceStatus
92
+ transaction.sourceTransactionHash = payload.data.sourceTransactionHash
93
+ transaction.sourceErrors = payload.data.sourceErrors
94
+
95
+ transaction.targetStatus = payload.data.targetStatus
96
+ transaction.targetTransactionHash = payload.data.targetTransactionHash
97
+ transaction.targetErrors = payload.data.targetErrors
98
+
99
+ transaction.status = payload.data.status
100
+
101
+ await transaction.save()
102
+ })
60
103
  }
61
104
 
62
105
  main()
@@ -88,12 +88,13 @@ export const startPeer = async ({ }: IPeerOptions) => {
88
88
  libp2p: node
89
89
  })
90
90
 
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
- );
91
+ node.on("peer:discovery", (peer) => {
92
+ // logger.log(`Discovered peer ${peer}`)
93
+ }); // peer disc.
94
+
95
+ node.connectionManager.on("peer:connect", (connection) => {
96
+ // logger.log(`Connected to ${connection.remotePeer.toB58String()}`)
97
+ });
97
98
 
98
99
  logger.log("Peer discovery started");
99
100
 
@@ -1,5 +1,10 @@
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
+
6
+
7
+ const logger = new Logger('PeerPool')
3
8
 
4
9
  export interface IPeerInfo {
5
10
  id: string;
@@ -75,10 +80,15 @@ export class PeerPool {
75
80
  * @emits {@link Event.POOL_PEER_ADDED}
76
81
  */
77
82
  add(peer?: IPeerInfo) {
78
- if (peer && peer.id && !this.pool.get(peer.id)) {
83
+ if (peer && peer.id) {
84
+ const newPeer = !this.pool.get(peer.id);
79
85
  this.pool.set(peer.id, peer)
80
86
  peer.pooled = true
81
- config.events.emit(Event.POOL_PEER_ADDED, peer)
87
+
88
+ if (newPeer) {
89
+ config.events.emit(Event.POOL_PEER_ADDED, peer)
90
+ logger.info(`Peer ${peer.id} with address ${peer.publicAddress} added to pool`)
91
+ }
82
92
  }
83
93
  }
84
94
 
@@ -92,6 +102,7 @@ export class PeerPool {
92
102
  if (this.pool.delete(peer.id)) {
93
103
  peer.pooled = false
94
104
  config.events.emit(Event.POOL_PEER_REMOVED, peer)
105
+ logger.info(`Peer ${peer.id} with address ${peer.publicAddress} removed from pool`)
95
106
  }
96
107
  }
97
108
  }
@@ -100,7 +111,7 @@ export class PeerPool {
100
111
  this.cleanup()
101
112
 
102
113
  return this.peers.filter((p) => {
103
- if(!p.pooled) return false;
114
+ if (!p.pooled) return false;
104
115
 
105
116
  const now = new Date()
106
117
 
@@ -113,15 +124,30 @@ export class PeerPool {
113
124
  }
114
125
 
115
126
 
116
- cleanup() {
117
- let compDate = Date.now() - this.PEERS_CLEANUP_TIME_LIMIT * 60
127
+ getPeer(id: string){
128
+ return this.pool.get(id);
129
+ }
118
130
 
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
- })
131
+ isLeadNode(id: string) {
132
+ const peer = this.pool.get(id);
133
+
134
+ if (!peer) {
135
+ return false;
136
+ }
137
+
138
+ return getAddress(peer.publicAddress) === getAddress(config.leadNodeAddress)
139
+ }
140
+
141
+
142
+ cleanup() {
143
+ // let compDate = Date.now() - this.PEERS_CLEANUP_TIME_LIMIT * 60
144
+
145
+ // this.peers.forEach((peerInfo) => {
146
+ // if (peerInfo.updated.getTime() < compDate) {
147
+ // console.log(`Peer ${peerInfo.id} idle for ${this.PEERS_CLEANUP_TIME_LIMIT} minutes`)
148
+ // this.remove(peerInfo)
149
+ // }
150
+ // })
125
151
  }
126
152
  }
127
153
 
@@ -2,9 +2,12 @@ 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";
6
+ import { addresses } from "@/constants";
7
+ import { ChainId } from "@/types";
5
8
 
6
9
  export interface ISignatureRequest {
7
- type: string,
10
+ type: 'source' | 'target' ,
8
11
  transactionHash: string
9
12
  safeTxGas: string
10
13
  safeNonce: string
@@ -24,19 +27,19 @@ export class SignatureDialProtocol extends BaseDialProtocol<ISignatureRequest, I
24
27
  async response(data: ISignatureRequest): Promise<ISignatureResponse> {
25
28
  const signer = config.wallet;
26
29
 
27
- let event: Transaction | null;
30
+ let transaction: Transaction | null;
28
31
  let maxTimeout = 20000;
29
32
 
30
33
  do {
31
- event = await Transaction.findOne({ where: { transactionHash: data.transactionHash } })
34
+ transaction = await Transaction.findOne({ where: { transactionHash: data.transactionHash } })
32
35
 
33
- if (!event) {
36
+ if (!transaction) {
34
37
  await wait(1000);
35
38
  maxTimeout -= 1000;
36
39
  }
37
- } while (!event && maxTimeout > 0)
40
+ } while (!transaction && maxTimeout > 0)
38
41
 
39
- if (!event) {
42
+ if (!transaction) {
40
43
  return {
41
44
  signer: signer.address,
42
45
  data: null,
@@ -44,16 +47,25 @@ export class SignatureDialProtocol extends BaseDialProtocol<ISignatureRequest, I
44
47
  };
45
48
  }
46
49
 
47
- // const signedData = await signGnosisSafeTx({
48
- // to: addresses[event.chainId as ChainId].multisend,
49
- // data: 'TODO',
50
- // chainId: event.chainId as ChainId,
51
- // safeTxGas: data.safeTxGas,
52
- // }, { signer });
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
+
58
+ const signedData = await signGnosisSafeTx({
59
+ to: addresses[transaction.targetChainId].multisend,
60
+ data: await buildDataForTransaction(transaction, data.type),
61
+ chainId: transaction.targetChainId as ChainId,
62
+ safeTxGas: data.safeTxGas,
63
+ nonce: data.safeNonce,
64
+ }, { signer });
53
65
 
54
66
  return {
55
67
  signer: signer.address,
56
- data: null, //signedData,
68
+ data: signedData
57
69
  }
58
70
  }
59
71
  }
@@ -5,6 +5,7 @@ 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";
8
9
 
9
10
  export interface ProtocolOptions {
10
11
  /* Handshake timeout in ms (default: 8000) */
@@ -33,7 +34,12 @@ interface PeerInfoEvent extends BaseMessageEvent {
33
34
  data: Omit<IPeerInfo, 'id' | 'updated' | 'idle' | 'pooled'>
34
35
  }
35
36
 
37
+ interface TransactionStatusEvent extends BaseMessageEvent {
38
+ data: Pick<Transaction, 'transactionHash' | 'sourceStatus' | 'sourceTransactionHash' | 'sourceErrors' | 'targetStatus' | 'targetTransactionHash' | 'targetErrors' | 'status'>
39
+ }
40
+
36
41
  declare interface Protocol {
42
+ on(event: 'TransactionStatus', listener: (payload: TransactionStatusEvent) => void): this;
37
43
  on(event: 'PeerInfo', listener: (payload: PeerInfoEvent) => void): this;
38
44
  on(event: string, listener: (payload: BaseMessageEvent) => void): this;
39
45
  }
@@ -44,7 +50,7 @@ class Protocol extends EventEmitter {
44
50
  private protocolMessages: Message[] = [
45
51
  {
46
52
  name: 'PeerInfo',
47
- code: 0x09,
53
+ code: 0x01,
48
54
  encode: (info: Pick<IPeerInfo, 'publicAddress'>) => [
49
55
  Buffer.from(info.publicAddress),
50
56
  ],
@@ -52,6 +58,36 @@ class Protocol extends EventEmitter {
52
58
  publicAddress: publicAddress.toString(),
53
59
  }),
54
60
  },
61
+ {
62
+ name: 'TransactionStatus',
63
+ code: 0x02,
64
+ encode: (transaction: Transaction) => [
65
+ Buffer.from(transaction.transactionHash),
66
+
67
+ Buffer.from(transaction.sourceStatus),
68
+ Buffer.from(transaction.sourceTransactionHash),
69
+ transaction.sourceErrors ? transaction.sourceErrors.map((e) => Buffer.from(e)) : [],
70
+
71
+ Buffer.from(transaction.targetStatus),
72
+ Buffer.from(transaction.targetTransactionHash),
73
+ transaction.targetErrors ? transaction.targetErrors.map((e) => Buffer.from(e)) : [],
74
+
75
+ Buffer.from(transaction.status),
76
+ ],
77
+ decode: ([transactionHash, sourceStatus, sourceTransactionHash, sourceErrors, targetStatus, targetTransactionHash, targetErrors, status]: [Buffer, Buffer, Buffer, Buffer[], Buffer, Buffer, Buffer[], Buffer]) => ({
78
+ transactionHash: transactionHash.toString(),
79
+
80
+ sourceStatus: sourceStatus.toString(),
81
+ sourceTransactionHash: sourceTransactionHash.toString(),
82
+ sourceErrors: sourceErrors.map((e) => e.toString()),
83
+
84
+ targetStatus: targetStatus.toString(),
85
+ targetTransactionHash: targetTransactionHash.toString(),
86
+ targetErrors: targetErrors.map((e) => e.toString()),
87
+
88
+ status: status.toString(),
89
+ }),
90
+ },
55
91
  ];
56
92
  private signature: SignatureDialProtocol;
57
93
 
@@ -121,6 +157,14 @@ class Protocol extends EventEmitter {
121
157
  this.libp2p.pubsub.publish(this.topic, encoded)
122
158
  }
123
159
 
160
+ public sendTransaction(transaction: Transaction) {
161
+ const message = this.protocolMessages.find((m) => m.name === 'TransactionStatus')!
162
+
163
+ const encoded = rlp.encode([message.code, message.encode(transaction)]);
164
+
165
+ this.libp2p.pubsub.publish(this.topic, encoded)
166
+ }
167
+
124
168
  async requestSignatures(data: ISignatureRequest, peerIds?: string[]) {
125
169
  try {
126
170
  peerIds = peerIds || peerPool.activePeerIds;
@@ -0,0 +1,60 @@
1
+ import { BaseTask } from "./BaseTask";
2
+ import Logger from '@/logger';
3
+ import { http } from "@/utils";
4
+ import spawn from 'await-spawn';
5
+ import config from "@/config";
6
+ import wait from "waait";
7
+
8
+ class AutoUpdateTask extends BaseTask {
9
+ pollIntervalMs: number = 60 * 5 * 1000
10
+
11
+ constructor() {
12
+ super({
13
+ logger: new Logger("AutoUpdateTask"),
14
+ })
15
+ }
16
+
17
+ prePollHandler(): boolean {
18
+ return config.autoUpdate && !config.isLeadNode();
19
+ }
20
+
21
+ getCurrentVersion() {
22
+ return require('../../package.json').version
23
+ }
24
+ async pollHandler() {
25
+
26
+ const { data } = await http.get('https://registry.npmjs.org/@instadapp/interop-x')
27
+
28
+ const version = data['dist-tags'].latest
29
+ const currentVersion = this.getCurrentVersion()
30
+
31
+ if (version === currentVersion) {
32
+ return;
33
+ }
34
+
35
+ this.logger.warn(`New version ${version} available.`)
36
+
37
+
38
+ await spawn('npm', ['-g', 'install', '@instadapp/interop-x', '-f']);
39
+
40
+
41
+ await wait(5000)
42
+
43
+ if (currentVersion === this.getCurrentVersion()) {
44
+ this.logger.warn(`failed to install ${version}, retrying in 5 minutes`)
45
+ return;
46
+ }
47
+
48
+ this.logger.warn(`Installed version ${version}`)
49
+ this.logger.warn(`Restarting...`)
50
+
51
+ spawn(process.argv[0], process.argv.slice(1), {
52
+ cwd: process.cwd(),
53
+ stdio: "inherit"
54
+ });
55
+
56
+ process.exit()
57
+ }
58
+ }
59
+
60
+ export default AutoUpdateTask;
@@ -37,7 +37,7 @@ export class BaseTask extends EventEmitter implements IBaseTask {
37
37
  await this.pollHandler()
38
38
  }
39
39
  } catch (err) {
40
- this.logger.error(`poll check error: ${err.message}\ntrace: ${err.stack}`)
40
+ this.logger.error(`poll check error:\n${err.message}\ntrace: ${err.stack}`)
41
41
  }
42
42
 
43
43
  await this.postPollHandler()
@@ -0,0 +1,233 @@
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 { buildDataForTransaction, buildSignatureBytes, getContract, getRpcProviderUrl, Signature } from "@/utils";
7
+ import { addresses } from "@/constants";
8
+ import { ChainId } from "@/types";
9
+ import config from "@/config";
10
+ import { GnosisSafe, InteropXGateway } 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
+
16
+ const generateGnosisTransaction = async (transactionData: any, safeContract: GnosisSafe) => {
17
+ console.log(transactionData);
18
+
19
+ let isExecuted = await safeContract.dataHashes(
20
+ await safeContract.getTransactionHash(
21
+ transactionData.to,
22
+ transactionData.value,
23
+ transactionData.data,
24
+ transactionData.operation,
25
+ transactionData.safeTxGas,
26
+ transactionData.baseGas,
27
+ transactionData.gasPrice,
28
+ transactionData.gasToken,
29
+ transactionData.refundReceiver,
30
+ transactionData.nonce
31
+ )
32
+ )
33
+
34
+ while (isExecuted == 1) {
35
+ transactionData.safeTxGas = BigNumber.from(String(transactionData.safeTxGas)).add(1).toString()
36
+
37
+ isExecuted = await safeContract.dataHashes(
38
+ await safeContract.getTransactionHash(
39
+ transactionData.to,
40
+ transactionData.value,
41
+ transactionData.data,
42
+ transactionData.operation,
43
+ transactionData.safeTxGas,
44
+ transactionData.baseGas,
45
+ transactionData.gasPrice,
46
+ transactionData.gasToken,
47
+ transactionData.refundReceiver,
48
+ transactionData.nonce
49
+ )
50
+ )
51
+ }
52
+
53
+ return transactionData
54
+ }
55
+
56
+ class ProcessWithdrawEvents extends BaseTask {
57
+ provider: ethers.providers.JsonRpcProvider;
58
+ chainId: ChainId;
59
+ leadNodeOnly = true
60
+
61
+ constructor({ chainId }: { chainId: ChainId }) {
62
+ super({
63
+ logger: new Logger("InteropXGateway::ProcessWithdrawEvents"),
64
+ })
65
+ this.chainId = chainId;
66
+ }
67
+
68
+ async pollHandler() {
69
+ const blockNumber = await this.provider.getBlockNumber()
70
+
71
+ const transaction = await Transaction.findOne({
72
+ where: {
73
+ status: 'pending',
74
+ sourceStatus: 'success',
75
+ targetStatus: 'uninitialised',
76
+ action: 'withdraw',
77
+ sourceCreatedAt: {
78
+ [Op.gte]: new Date(Date.now() - 12 * 60 * 60 * 1000),
79
+ },
80
+ targetDelayUntil: {
81
+ [Op.or]: {
82
+ [Op.is]: null,
83
+ [Op.lt]: new Date(),
84
+ }
85
+ },
86
+ sourceBlockNumber: {
87
+ [Op.lt]: blockNumber - 12,
88
+ },
89
+ sourceChainId: this.chainId,
90
+ }
91
+ })
92
+
93
+ if (!transaction) {
94
+ return;
95
+ }
96
+
97
+ console.log(`Processing transaction ${transaction.transactionHash}`);
98
+
99
+ transaction.targetStatus = 'pending';
100
+ await transaction.save();
101
+
102
+ // refresh event data?
103
+
104
+ const targetChainProvider = new ethers.providers.JsonRpcProvider(
105
+ getRpcProviderUrl(transaction.targetChainId as ChainId)
106
+ );
107
+
108
+ const targetWallet = new ethers.Wallet(config.privateKey!, targetChainProvider);
109
+
110
+ const safeAddress = addresses[transaction.targetChainId].gnosisSafe;
111
+
112
+
113
+ const safeContract = getContract<GnosisSafe>(
114
+ safeAddress,
115
+ abi.gnosisSafe,
116
+ targetWallet
117
+ )
118
+
119
+ const ownersThreshold = await safeContract.getThreshold();
120
+ await wait(10000);
121
+
122
+ let gnosisTx = await generateGnosisTransaction({
123
+ baseGas: "0",
124
+ data: await buildDataForTransaction(transaction),
125
+ gasPrice: "0",
126
+ gasToken: "0x0000000000000000000000000000000000000000",
127
+ nonce: '0',
128
+ operation: "1",
129
+ refundReceiver: "0x0000000000000000000000000000000000000000",
130
+ safeAddress: safeAddress,
131
+ safeTxGas: "79668",
132
+ to: addresses[transaction.targetChainId].multisend,
133
+ value: "0",
134
+ }, safeContract);
135
+
136
+ const owners = await safeContract.getOwners().then(owners => owners.map(owner => owner.toLowerCase()));
137
+
138
+ const ownerPeerIds = peerPool.activePeers.filter(peer => owners.includes(peer.publicAddress.toLowerCase())).map(peer => peer.id)
139
+
140
+ console.log(`Collecting signatures for execution ${transaction.transactionHash}`)
141
+
142
+ console.log(ownerPeerIds);
143
+
144
+ const signatures = await protocol.requestSignatures({
145
+ type: 'source',
146
+ transactionHash: transaction.transactionHash,
147
+ safeTxGas: gnosisTx.safeTxGas,
148
+ safeNonce: gnosisTx.nonce
149
+ }, ownerPeerIds)
150
+
151
+
152
+ const validSignatures = signatures.filter(s => !!s.data && s.data !== '0x') as Signature[];
153
+
154
+ console.log({ signatures, validSignatures, ownersThreshold: ownersThreshold.toString() });
155
+
156
+ if (validSignatures.length === 0 || ownersThreshold.gt(validSignatures.length)) {
157
+ await transaction.save();
158
+ transaction.targetDelayUntil = new Date(Date.now() + 30 * 1000);
159
+ transaction.targetStatus = 'uninitialised'
160
+
161
+ await transaction.save();
162
+ const errorMessage = signatures.find(s => !!s.error)?.error;
163
+ throw new Error(`Not enough signatures` + (errorMessage ? `: ${errorMessage}` : ''));
164
+ }
165
+
166
+
167
+ console.log(`Executing transaction for execution ${transaction.transactionHash}`)
168
+
169
+ const { data: txData } = await safeContract.populateTransaction.execTransaction(
170
+ gnosisTx.to,
171
+ gnosisTx.value,
172
+ gnosisTx.data,
173
+ gnosisTx.operation,
174
+ gnosisTx.safeTxGas,
175
+ gnosisTx.baseGas,
176
+ gnosisTx.gasPrice,
177
+ gnosisTx.gasToken,
178
+ gnosisTx.refundReceiver,
179
+ buildSignatureBytes(validSignatures)
180
+ );
181
+
182
+ console.log({
183
+ from: targetWallet.address,
184
+ gasPrice: BigNumber.from(120 * 10 ** 9).toString(),
185
+ to: safeAddress,
186
+ data: txData,
187
+ })
188
+
189
+
190
+ const txSent = await targetWallet.sendTransaction({
191
+ from: targetWallet.address,
192
+ gasPrice: BigNumber.from(120 * 10 ** 9),
193
+ to: safeAddress,
194
+ data: txData,
195
+ })
196
+
197
+ const receipt = await txSent.wait();
198
+
199
+ const parsedLogs: LogDescription[] = [];
200
+
201
+ receipt.logs.forEach((log) => {
202
+ try {
203
+ parsedLogs.push(safeContract.interface.parseLog(log));
204
+ } catch (e) { }
205
+ });
206
+
207
+ if (parsedLogs.find(e => e.name === 'ExecutionSuccess')) {
208
+ console.log('ExecutionSuccess')
209
+ transaction.targetStatus = 'success'
210
+ transaction.targetTransactionHash = txSent.hash
211
+ transaction.status = 'success'
212
+ await transaction.save();
213
+ } else {
214
+ console.log('ExecutionFailure')
215
+ transaction.targetStatus = 'failed'
216
+ transaction.targetTransactionHash = txSent.hash
217
+ transaction.status = 'failed'
218
+ await transaction.save();
219
+ }
220
+ }
221
+
222
+ async start(): Promise<void> {
223
+ this.logger.info(`Starting execution watcher on interop chain`);
224
+
225
+ this.provider = new ethers.providers.JsonRpcProvider(
226
+ getRpcProviderUrl(this.chainId)
227
+ );
228
+
229
+ await super.start()
230
+ }
231
+ }
232
+
233
+ export default ProcessWithdrawEvents;