@instadapp/interop-x 0.0.0-dev.99c56eb → 0.0.0-dev.99c6cb0

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.
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instadapp/interop-x",
3
- "version": "0.0.0-dev.99c56eb",
3
+ "version": "0.0.0-dev.99c6cb0",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "engines": {
@@ -27,15 +27,15 @@
27
27
  "@fastify/cors": "^7.0.0",
28
28
  "async-retry": "^1.3.3",
29
29
  "await-spawn": "^4.0.2",
30
- "axios": "^0.27.1",
31
- "axios-retry": "^3.2.4",
30
+ "axios": "^0.27.2",
31
+ "axios-retry": "^3.2.5",
32
32
  "chalk": "4.1.2",
33
- "dotenv": "^16.0.0",
33
+ "dotenv": "^16.0.1",
34
34
  "ethereumjs-util": "^7.1.4",
35
- "ethers": "^5.6.4",
35
+ "ethers": "^5.6.5",
36
36
  "ethers-multisend": "^2.1.1",
37
37
  "expand-home-dir": "^0.0.3",
38
- "fastify": "^3.28.0",
38
+ "fastify": "^3.29.0",
39
39
  "fs-extra": "^10.1.0",
40
40
  "libp2p": "^0.36.2",
41
41
  "libp2p-bootstrap": "^0.14.0",
@@ -46,12 +46,12 @@
46
46
  "libp2p-pubsub-peer-discovery": "^4.0.0",
47
47
  "libp2p-tcp": "^0.17.2",
48
48
  "libp2p-websockets": "^0.16.2",
49
- "luxon": "^2.3.2",
49
+ "luxon": "^2.4.0",
50
50
  "module-alias": "^2.2.2",
51
51
  "patch-package": "^6.4.7",
52
52
  "postinstall-postinstall": "^2.1.0",
53
53
  "sequelize": "6.18.0",
54
- "sqlite3": "^5.0.5",
54
+ "sqlite3": "^5.0.8",
55
55
  "waait": "^1.0.5"
56
56
  },
57
57
  "bin": {
@@ -62,13 +62,13 @@
62
62
  "@typechain/ethers-v5": "^10.0.0",
63
63
  "@types/bn.js": "^5.1.0",
64
64
  "@types/fs-extra": "^9.0.13",
65
- "@types/node": "^17.0.17",
66
- "nodemon": "^2.0.15",
65
+ "@types/node": "^17.0.33",
66
+ "nodemon": "^2.0.16",
67
67
  "replace-in-file": "^6.3.2",
68
68
  "rimraf": "^3.0.2",
69
69
  "ts-node": "^10.5.0",
70
70
  "tsconfig-paths": "^3.12.0",
71
71
  "typechain": "^8.0.0",
72
- "typescript": "^4.5.5"
72
+ "typescript": "^4.6.4"
73
73
  }
74
74
  }
@@ -2,6 +2,12 @@
2
2
  {
3
3
  "anonymous": false,
4
4
  "inputs": [
5
+ {
6
+ "indexed": false,
7
+ "internalType": "string",
8
+ "name": "actionId",
9
+ "type": "string"
10
+ },
5
11
  {
6
12
  "indexed": true,
7
13
  "internalType": "address",
@@ -83,6 +89,12 @@
83
89
  {
84
90
  "anonymous": false,
85
91
  "inputs": [
92
+ {
93
+ "indexed": false,
94
+ "internalType": "string",
95
+ "name": "actionId",
96
+ "type": "string"
97
+ },
86
98
  {
87
99
  "indexed": true,
88
100
  "internalType": "address",
@@ -170,6 +182,12 @@
170
182
  {
171
183
  "anonymous": false,
172
184
  "inputs": [
185
+ {
186
+ "indexed": false,
187
+ "internalType": "string",
188
+ "name": "actionId",
189
+ "type": "string"
190
+ },
173
191
  {
174
192
  "indexed": true,
175
193
  "internalType": "address",
@@ -245,6 +263,12 @@
245
263
  {
246
264
  "anonymous": false,
247
265
  "inputs": [
266
+ {
267
+ "indexed": false,
268
+ "internalType": "string",
269
+ "name": "actionId",
270
+ "type": "string"
271
+ },
248
272
  {
249
273
  "indexed": true,
250
274
  "internalType": "address",
@@ -323,20 +347,14 @@
323
347
  "name": "LogBridgeRequestSent",
324
348
  "type": "event"
325
349
  },
326
- {
327
- "inputs": [],
328
- "name": "_vnonce",
329
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
330
- "stateMutability": "view",
331
- "type": "function"
332
- },
333
350
  {
334
351
  "inputs": [
352
+ { "internalType": "string", "name": "actionId", "type": "string" },
335
353
  { "internalType": "address", "name": "to_", "type": "address" },
336
354
  { "internalType": "address", "name": "sourceToken_", "type": "address" },
337
355
  { "internalType": "address", "name": "targetToken_", "type": "address" },
338
356
  { "internalType": "uint256", "name": "amount_", "type": "uint256" },
339
- { "internalType": "uint32", "name": "targetChainId_", "type": "uint32" },
357
+ { "internalType": "uint32", "name": "sourceChainId_", "type": "uint32" },
340
358
  {
341
359
  "internalType": "bytes32",
342
360
  "name": "transactionHash_",
@@ -351,6 +369,27 @@
351
369
  },
352
370
  {
353
371
  "inputs": [
372
+ { "internalType": "address", "name": "user", "type": "address" },
373
+ { "internalType": "address[]", "name": "tokens", "type": "address[]" }
374
+ ],
375
+ "name": "getBridgeAmounts",
376
+ "outputs": [
377
+ {
378
+ "components": [
379
+ { "internalType": "uint256", "name": "deposit", "type": "uint256" },
380
+ { "internalType": "uint256", "name": "withdraw", "type": "uint256" }
381
+ ],
382
+ "internalType": "struct InteropXContractBeta.UserData[]",
383
+ "name": "userData",
384
+ "type": "tuple[]"
385
+ }
386
+ ],
387
+ "stateMutability": "view",
388
+ "type": "function"
389
+ },
390
+ {
391
+ "inputs": [
392
+ { "internalType": "string", "name": "actionId", "type": "string" },
354
393
  { "internalType": "address", "name": "sourceToken_", "type": "address" },
355
394
  { "internalType": "address", "name": "targetToken_", "type": "address" },
356
395
  { "internalType": "uint256", "name": "amount_", "type": "uint256" },
@@ -362,15 +401,39 @@
362
401
  "stateMutability": "nonpayable",
363
402
  "type": "function"
364
403
  },
404
+ {
405
+ "inputs": [
406
+ { "internalType": "address", "name": "token", "type": "address" },
407
+ { "internalType": "bool", "name": "toggle", "type": "bool" }
408
+ ],
409
+ "name": "toggleWhitelist",
410
+ "outputs": [],
411
+ "stateMutability": "nonpayable",
412
+ "type": "function"
413
+ },
414
+ {
415
+ "inputs": [
416
+ { "internalType": "address", "name": "", "type": "address" },
417
+ { "internalType": "address", "name": "", "type": "address" }
418
+ ],
419
+ "name": "userMapping",
420
+ "outputs": [
421
+ { "internalType": "uint256", "name": "deposit", "type": "uint256" },
422
+ { "internalType": "uint256", "name": "withdraw", "type": "uint256" }
423
+ ],
424
+ "stateMutability": "view",
425
+ "type": "function"
426
+ },
365
427
  {
366
428
  "inputs": [{ "internalType": "address", "name": "", "type": "address" }],
367
- "name": "whitelistedMapping",
368
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
429
+ "name": "whitelistedTokens",
430
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
369
431
  "stateMutability": "view",
370
432
  "type": "function"
371
433
  },
372
434
  {
373
435
  "inputs": [
436
+ { "internalType": "string", "name": "actionId", "type": "string" },
374
437
  { "internalType": "address", "name": "user_", "type": "address" },
375
438
  { "internalType": "address", "name": "sourceToken_", "type": "address" },
376
439
  { "internalType": "address", "name": "targetToken_", "type": "address" },
@@ -10,11 +10,11 @@ exports.addresses = {
10
10
  137: {
11
11
  gnosisSafe: '0x5635d2910e51da33d9DC0422c893CF4F28B69A25',
12
12
  multisend: '0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761',
13
- interopXContract: '0x2189741c8cAc1cf51570932817A7d6390646C80e',
13
+ interopXContract: '0x41bd77583674B3a5c073FBb4922C0C8dA91C43cF',
14
14
  },
15
15
  43114: {
16
16
  gnosisSafe: '0x31d7a5194Fe60AC209Cf1Ce2d539C9A60662Ed6b',
17
17
  multisend: '0x998739BFdAAdde7C933B942a68053933098f9EDa',
18
- interopXContract: '0x1867DF97Ec24bb0bbD4AD464F0Be9C6713422EAE',
18
+ interopXContract: '0xCF391A3057eDba541c80307aC5E3Bc1E4a9471b7',
19
19
  }
20
20
  };
@@ -15,7 +15,7 @@ Transaction.init({
15
15
  requestTransactionHash: sequelize_2.DataTypes.NUMBER,
16
16
  requestBlockNumber: sequelize_2.DataTypes.NUMBER,
17
17
  transactionHash: sequelize_2.DataTypes.STRING,
18
- action: sequelize_2.DataTypes.STRING,
18
+ actionId: sequelize_2.DataTypes.STRING,
19
19
  bridger: sequelize_2.DataTypes.STRING,
20
20
  sourceChainId: sequelize_2.DataTypes.NUMBER,
21
21
  sourceTransactionHash: sequelize_2.DataTypes.STRING,
@@ -12,8 +12,8 @@ const ethers_multisend_1 = require("ethers-multisend");
12
12
  async function default_1(transaction, type) {
13
13
  const transactions = [];
14
14
  const logs = [];
15
- if (transaction.action !== 'withdraw') {
16
- throw new Error(`Invalid action: ${transaction.action}`);
15
+ if (transaction.actionId !== 'withdraw') {
16
+ throw new Error(`Invalid action: ${transaction.actionId}`);
17
17
  }
18
18
  if (type !== 'source') {
19
19
  throw new Error(`[WIP] Type not supported: ${type}`);
@@ -27,7 +27,7 @@ async function default_1(transaction, type) {
27
27
  if (!transaction.requestEvent) {
28
28
  throw Error('Something went wrong, source transaction has no request event');
29
29
  }
30
- const { bridger, position, sourceChainId, targetChainId, metadata, } = transaction.requestEvent;
30
+ const { actionId, bridger, position, sourceChainId, targetChainId, metadata } = transaction.requestEvent;
31
31
  const sourceChainProvider = new ethers_1.ethers.providers.JsonRpcProvider((0, utils_1.getRpcProviderUrl)(targetChainId));
32
32
  const sourceWallet = new ethers_1.ethers.Wallet(config_1.default.privateKey, sourceChainProvider);
33
33
  const contractAddress = constants_1.addresses[sourceChainId].interopXContract;
@@ -43,7 +43,7 @@ async function default_1(transaction, type) {
43
43
  if (sourceToken.aliases.some(alias => targetToken.aliases.includes(alias))) {
44
44
  throw Error('Source and target token must be the same');
45
45
  }
46
- const { data } = await contract.populateTransaction.withdrawRequested(bridger, position.withdraw[0].sourceToken, position.withdraw[0].targetToken, position.withdraw[0].amount, targetChainId, transaction.requestTransactionHash, metadata);
46
+ const { data } = await contract.populateTransaction.withdrawRequested(actionId, bridger, position.withdraw[0].sourceToken, position.withdraw[0].targetToken, position.withdraw[0].amount, targetChainId, transaction.requestTransactionHash, metadata);
47
47
  transactions.push({
48
48
  to: contractAddress,
49
49
  data: data,
@@ -8,13 +8,13 @@ const ethers_multisend_1 = require("ethers-multisend");
8
8
  const actions_1 = __importDefault(require("./actions"));
9
9
  const buildGnosisAction = async (transaction, type) => {
10
10
  // type = type || (transaction.sourceStatus === 'success' ? 'target' : 'source')
11
- if (actions_1.default.hasOwnProperty(transaction.action)) {
12
- const { transactions, logs } = await actions_1.default[transaction.action](transaction, type);
11
+ if (actions_1.default.hasOwnProperty(transaction.actionId)) {
12
+ const { transactions, logs } = await actions_1.default[transaction.actionId](transaction, type);
13
13
  return {
14
14
  data: (0, ethers_multisend_1.encodeMulti)(transactions).data,
15
15
  logs
16
16
  };
17
17
  }
18
- throw new Error(`Unknown action: ${transaction.action}`);
18
+ throw new Error(`Unknown action: ${transaction.actionId}`);
19
19
  };
20
20
  exports.buildGnosisAction = buildGnosisAction;
package/dist/src/index.js CHANGED
@@ -13,7 +13,7 @@ const package_json_1 = __importDefault(require("../package.json"));
13
13
  dotenv_1.default.config();
14
14
  const logger_1 = __importDefault(require("@/logger"));
15
15
  const logger = new logger_1.default('Process');
16
- const GIT_SHORT_HASH = '99c56eb';
16
+ const GIT_SHORT_HASH = '99c6cb0';
17
17
  const printUsage = () => {
18
18
  console.log();
19
19
  console.log(`Interop X Node (v${package_json_1.default.version} - rev.${GIT_SHORT_HASH})`);
@@ -27,9 +27,9 @@ class SyncBridgeRequestEvents extends BaseTask_1.BaseTask {
27
27
  if (!event.args) {
28
28
  continue;
29
29
  }
30
- const { bridger, position, sourceChainId, targetChainId, metadata } = event.args;
30
+ const { actionId, bridger, position, sourceChainId, targetChainId, metadata } = event.args;
31
31
  const uniqueIdentifier = {
32
- action: 'withdraw',
32
+ actionId,
33
33
  bridger,
34
34
  requestTransactionHash: event.transactionHash,
35
35
  sourceChainId: sourceChainId.toNumber(),
@@ -41,6 +41,7 @@ class SyncBridgeRequestEvents extends BaseTask_1.BaseTask {
41
41
  continue;
42
42
  }
43
43
  await db_1.Transaction.create(Object.assign(Object.assign({ transactionHash }, uniqueIdentifier), { requestBlockNumber: event.blockNumber, requestEvent: {
44
+ actionId,
44
45
  bridger,
45
46
  position: {
46
47
  withdraw: position.withdraw.map((v) => ({
@@ -28,9 +28,9 @@ class SyncBridgeRequestSentEvents extends BaseTask_1.BaseTask {
28
28
  if (!event.args) {
29
29
  continue;
30
30
  }
31
- const { bridger, position, sourceChainId, targetChainId, requestTransactionHash, metadata } = event.args;
31
+ const { actionId, bridger, position, sourceChainId, targetChainId, requestTransactionHash, metadata } = event.args;
32
32
  const uniqueIdentifier = {
33
- action: 'withdraw',
33
+ actionId,
34
34
  bridger,
35
35
  requestTransactionHash,
36
36
  sourceChainId: sourceChainId,
@@ -51,6 +51,7 @@ class SyncBridgeRequestSentEvents extends BaseTask_1.BaseTask {
51
51
  transaction.sourceBlockNumber = event.blockNumber;
52
52
  transaction.sourceTransactionHash = event.transactionHash;
53
53
  transaction.requestSentEvent = {
54
+ actionId,
54
55
  bridger,
55
56
  position: {
56
57
  withdraw: position.withdraw.map((v) => ({
@@ -9,6 +9,12 @@ const _abi = [
9
9
  {
10
10
  anonymous: false,
11
11
  inputs: [
12
+ {
13
+ indexed: false,
14
+ internalType: "string",
15
+ name: "actionId",
16
+ type: "string",
17
+ },
12
18
  {
13
19
  indexed: true,
14
20
  internalType: "address",
@@ -98,6 +104,12 @@ const _abi = [
98
104
  {
99
105
  anonymous: false,
100
106
  inputs: [
107
+ {
108
+ indexed: false,
109
+ internalType: "string",
110
+ name: "actionId",
111
+ type: "string",
112
+ },
101
113
  {
102
114
  indexed: true,
103
115
  internalType: "address",
@@ -193,6 +205,12 @@ const _abi = [
193
205
  {
194
206
  anonymous: false,
195
207
  inputs: [
208
+ {
209
+ indexed: false,
210
+ internalType: "string",
211
+ name: "actionId",
212
+ type: "string",
213
+ },
196
214
  {
197
215
  indexed: true,
198
216
  internalType: "address",
@@ -276,6 +294,12 @@ const _abi = [
276
294
  {
277
295
  anonymous: false,
278
296
  inputs: [
297
+ {
298
+ indexed: false,
299
+ internalType: "string",
300
+ name: "actionId",
301
+ type: "string",
302
+ },
279
303
  {
280
304
  indexed: true,
281
305
  internalType: "address",
@@ -363,20 +387,12 @@ const _abi = [
363
387
  type: "event",
364
388
  },
365
389
  {
366
- inputs: [],
367
- name: "_vnonce",
368
- outputs: [
390
+ inputs: [
369
391
  {
370
- internalType: "uint256",
371
- name: "",
372
- type: "uint256",
392
+ internalType: "string",
393
+ name: "actionId",
394
+ type: "string",
373
395
  },
374
- ],
375
- stateMutability: "view",
376
- type: "function",
377
- },
378
- {
379
- inputs: [
380
396
  {
381
397
  internalType: "address",
382
398
  name: "to_",
@@ -399,7 +415,7 @@ const _abi = [
399
415
  },
400
416
  {
401
417
  internalType: "uint32",
402
- name: "targetChainId_",
418
+ name: "sourceChainId_",
403
419
  type: "uint32",
404
420
  },
405
421
  {
@@ -420,6 +436,47 @@ const _abi = [
420
436
  },
421
437
  {
422
438
  inputs: [
439
+ {
440
+ internalType: "address",
441
+ name: "user",
442
+ type: "address",
443
+ },
444
+ {
445
+ internalType: "address[]",
446
+ name: "tokens",
447
+ type: "address[]",
448
+ },
449
+ ],
450
+ name: "getBridgeAmounts",
451
+ outputs: [
452
+ {
453
+ components: [
454
+ {
455
+ internalType: "uint256",
456
+ name: "deposit",
457
+ type: "uint256",
458
+ },
459
+ {
460
+ internalType: "uint256",
461
+ name: "withdraw",
462
+ type: "uint256",
463
+ },
464
+ ],
465
+ internalType: "struct InteropXContractBeta.UserData[]",
466
+ name: "userData",
467
+ type: "tuple[]",
468
+ },
469
+ ],
470
+ stateMutability: "view",
471
+ type: "function",
472
+ },
473
+ {
474
+ inputs: [
475
+ {
476
+ internalType: "string",
477
+ name: "actionId",
478
+ type: "string",
479
+ },
423
480
  {
424
481
  internalType: "address",
425
482
  name: "sourceToken_",
@@ -451,6 +508,24 @@ const _abi = [
451
508
  stateMutability: "nonpayable",
452
509
  type: "function",
453
510
  },
511
+ {
512
+ inputs: [
513
+ {
514
+ internalType: "address",
515
+ name: "token",
516
+ type: "address",
517
+ },
518
+ {
519
+ internalType: "bool",
520
+ name: "toggle",
521
+ type: "bool",
522
+ },
523
+ ],
524
+ name: "toggleWhitelist",
525
+ outputs: [],
526
+ stateMutability: "nonpayable",
527
+ type: "function",
528
+ },
454
529
  {
455
530
  inputs: [
456
531
  {
@@ -458,20 +533,54 @@ const _abi = [
458
533
  name: "",
459
534
  type: "address",
460
535
  },
536
+ {
537
+ internalType: "address",
538
+ name: "",
539
+ type: "address",
540
+ },
461
541
  ],
462
- name: "whitelistedMapping",
542
+ name: "userMapping",
463
543
  outputs: [
464
544
  {
465
545
  internalType: "uint256",
466
- name: "",
546
+ name: "deposit",
467
547
  type: "uint256",
468
548
  },
549
+ {
550
+ internalType: "uint256",
551
+ name: "withdraw",
552
+ type: "uint256",
553
+ },
554
+ ],
555
+ stateMutability: "view",
556
+ type: "function",
557
+ },
558
+ {
559
+ inputs: [
560
+ {
561
+ internalType: "address",
562
+ name: "",
563
+ type: "address",
564
+ },
565
+ ],
566
+ name: "whitelistedTokens",
567
+ outputs: [
568
+ {
569
+ internalType: "bool",
570
+ name: "",
571
+ type: "bool",
572
+ },
469
573
  ],
470
574
  stateMutability: "view",
471
575
  type: "function",
472
576
  },
473
577
  {
474
578
  inputs: [
579
+ {
580
+ internalType: "string",
581
+ name: "actionId",
582
+ type: "string",
583
+ },
475
584
  {
476
585
  internalType: "address",
477
586
  name: "user_",
@@ -103,7 +103,7 @@ const asyncCallWithTimeout = async (asyncPromise, timeout) => {
103
103
  exports.asyncCallWithTimeout = asyncCallWithTimeout;
104
104
  const generateInteropTransactionHash = (data) => {
105
105
  return ethers_1.ethers.utils.solidityKeccak256(['string', 'string', 'string', 'string', 'string'], [
106
- String(data.action),
106
+ String(data.actionId),
107
107
  String(data.bridger),
108
108
  String(data.requestTransactionHash),
109
109
  String(data.sourceChainId),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instadapp/interop-x",
3
- "version": "0.0.0-dev.99c56eb",
3
+ "version": "0.0.0-dev.99c6cb0",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "engines": {
@@ -27,15 +27,15 @@
27
27
  "@fastify/cors": "^7.0.0",
28
28
  "async-retry": "^1.3.3",
29
29
  "await-spawn": "^4.0.2",
30
- "axios": "^0.27.1",
31
- "axios-retry": "^3.2.4",
30
+ "axios": "^0.27.2",
31
+ "axios-retry": "^3.2.5",
32
32
  "chalk": "4.1.2",
33
- "dotenv": "^16.0.0",
33
+ "dotenv": "^16.0.1",
34
34
  "ethereumjs-util": "^7.1.4",
35
- "ethers": "^5.6.4",
35
+ "ethers": "^5.6.5",
36
36
  "ethers-multisend": "^2.1.1",
37
37
  "expand-home-dir": "^0.0.3",
38
- "fastify": "^3.28.0",
38
+ "fastify": "^3.29.0",
39
39
  "fs-extra": "^10.1.0",
40
40
  "libp2p": "^0.36.2",
41
41
  "libp2p-bootstrap": "^0.14.0",
@@ -46,12 +46,12 @@
46
46
  "libp2p-pubsub-peer-discovery": "^4.0.0",
47
47
  "libp2p-tcp": "^0.17.2",
48
48
  "libp2p-websockets": "^0.16.2",
49
- "luxon": "^2.3.2",
49
+ "luxon": "^2.4.0",
50
50
  "module-alias": "^2.2.2",
51
51
  "patch-package": "^6.4.7",
52
52
  "postinstall-postinstall": "^2.1.0",
53
53
  "sequelize": "6.18.0",
54
- "sqlite3": "^5.0.5",
54
+ "sqlite3": "^5.0.8",
55
55
  "waait": "^1.0.5"
56
56
  },
57
57
  "bin": {
@@ -62,13 +62,13 @@
62
62
  "@typechain/ethers-v5": "^10.0.0",
63
63
  "@types/bn.js": "^5.1.0",
64
64
  "@types/fs-extra": "^9.0.13",
65
- "@types/node": "^17.0.17",
66
- "nodemon": "^2.0.15",
65
+ "@types/node": "^17.0.33",
66
+ "nodemon": "^2.0.16",
67
67
  "replace-in-file": "^6.3.2",
68
68
  "rimraf": "^3.0.2",
69
69
  "ts-node": "^10.5.0",
70
70
  "tsconfig-paths": "^3.12.0",
71
71
  "typechain": "^8.0.0",
72
- "typescript": "^4.5.5"
72
+ "typescript": "^4.6.4"
73
73
  }
74
74
  }