@dorafactory/maci-sdk 0.0.20 → 0.0.21
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/browser.d.mts +85 -5
- package/dist/browser.d.ts +85 -5
- package/dist/browser.js +380 -24
- package/dist/browser.js.map +1 -1
- package/dist/browser.mjs +380 -24
- package/dist/browser.mjs.map +1 -1
- package/dist/index.d.mts +85 -5
- package/dist/index.d.ts +85 -5
- package/dist/index.js +380 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +380 -24
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/libs/contract/contract.ts +32 -21
- package/src/libs/contract/types.ts +4 -4
- package/src/libs/contract/utils.ts +26 -6
- package/src/libs/errors/types.ts +3 -0
- package/src/libs/indexer/indexer.ts +21 -0
- package/src/libs/maci/maci.ts +60 -0
- package/src/libs/query/operator.ts +338 -0
- package/src/types/index.ts +86 -0
package/package.json
CHANGED
|
@@ -27,7 +27,11 @@ import {
|
|
|
27
27
|
CreateMaciRoundParams,
|
|
28
28
|
CreateOracleMaciRoundParams,
|
|
29
29
|
} from './types';
|
|
30
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
getAMaciRoundCircuitFee,
|
|
32
|
+
getCircuitType,
|
|
33
|
+
getContractParams,
|
|
34
|
+
} from './utils';
|
|
31
35
|
import { QTR_LIB } from './vars';
|
|
32
36
|
import { MaciRoundType, MaciCertSystemType } from '../../types';
|
|
33
37
|
import { decompressPublicKey } from '../../utils';
|
|
@@ -82,26 +86,33 @@ export class Contract {
|
|
|
82
86
|
contractAddress: this.registryAddress,
|
|
83
87
|
});
|
|
84
88
|
|
|
89
|
+
const requiredFee = getAMaciRoundCircuitFee(maxVoter, maxOption);
|
|
90
|
+
|
|
85
91
|
preDeactivateRoot = preDeactivateRoot || '0';
|
|
86
|
-
const res = await client.createRound(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
92
|
+
const res = await client.createRound(
|
|
93
|
+
{
|
|
94
|
+
operator,
|
|
95
|
+
preDeactivateRoot,
|
|
96
|
+
voiceCreditAmount,
|
|
97
|
+
whitelist,
|
|
98
|
+
roundInfo: {
|
|
99
|
+
title,
|
|
100
|
+
description: description || '',
|
|
101
|
+
link: link || '',
|
|
102
|
+
},
|
|
103
|
+
votingTime: {
|
|
104
|
+
start_time,
|
|
105
|
+
end_time,
|
|
106
|
+
},
|
|
107
|
+
maxVoter: maxVoter.toString(),
|
|
108
|
+
maxOption: maxOption.toString(),
|
|
109
|
+
certificationSystem: '0',
|
|
110
|
+
circuitType,
|
|
99
111
|
},
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
});
|
|
112
|
+
'auto',
|
|
113
|
+
undefined,
|
|
114
|
+
[requiredFee]
|
|
115
|
+
);
|
|
105
116
|
let contractAddress = '';
|
|
106
117
|
res.events.map((event) => {
|
|
107
118
|
if (event.type === 'wasm') {
|
|
@@ -208,8 +219,8 @@ export class Contract {
|
|
|
208
219
|
MaciRoundType.ORACLE_MACI,
|
|
209
220
|
circuitType,
|
|
210
221
|
MaciCertSystemType.GROTH16,
|
|
211
|
-
|
|
212
|
-
|
|
222
|
+
0,
|
|
223
|
+
0
|
|
213
224
|
);
|
|
214
225
|
const instantiateResponse = await client.instantiate(
|
|
215
226
|
address,
|
|
@@ -18,8 +18,8 @@ export type CreateRoundParams = {
|
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
export type CreateAMaciRoundParams = {
|
|
21
|
-
maxVoter:
|
|
22
|
-
maxOption:
|
|
21
|
+
maxVoter: number;
|
|
22
|
+
maxOption: number;
|
|
23
23
|
operator: string;
|
|
24
24
|
whitelist: RegistryWhitelist;
|
|
25
25
|
voiceCreditAmount: string;
|
|
@@ -27,8 +27,8 @@ export type CreateAMaciRoundParams = {
|
|
|
27
27
|
} & CreateRoundParams;
|
|
28
28
|
|
|
29
29
|
export type CreateMaciRoundParams = {
|
|
30
|
-
maxVoter:
|
|
31
|
-
maxOption:
|
|
30
|
+
maxVoter: number;
|
|
31
|
+
maxOption: number;
|
|
32
32
|
operatorPubkey: string;
|
|
33
33
|
whitelist: MaciWhitelist;
|
|
34
34
|
certSystemType: MaciCertSystemType;
|
|
@@ -50,8 +50,8 @@ export function getContractParams(
|
|
|
50
50
|
type: MaciRoundType,
|
|
51
51
|
circuitType: MaciCircuitType,
|
|
52
52
|
proofSystem: MaciCertSystemType,
|
|
53
|
-
maxVoter:
|
|
54
|
-
maxOption:
|
|
53
|
+
maxVoter: number,
|
|
54
|
+
maxOption: number
|
|
55
55
|
) {
|
|
56
56
|
let parameters: {
|
|
57
57
|
state_tree_depth: string;
|
|
@@ -92,7 +92,7 @@ export function getContractParams(
|
|
|
92
92
|
);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
if (
|
|
95
|
+
if (maxVoter <= 25 && maxOption <= 5) {
|
|
96
96
|
// state_tree_depth: 2
|
|
97
97
|
// vote_option_tree_depth: 1
|
|
98
98
|
parameters = CIRCUIT_INFO['2-1-1-5'].parameter;
|
|
@@ -103,7 +103,7 @@ export function getContractParams(
|
|
|
103
103
|
plonkProcessVkey = CIRCUIT_INFO['2-1-1-5']['plonk']?.process_vkey;
|
|
104
104
|
plonkTallyVkey = CIRCUIT_INFO['2-1-1-5']['plonk']?.tally_vkey;
|
|
105
105
|
}
|
|
106
|
-
} else if (
|
|
106
|
+
} else if (maxVoter <= 625 && maxOption <= 25) {
|
|
107
107
|
// state_tree_depth: 4
|
|
108
108
|
// vote_option_tree_depth: 2
|
|
109
109
|
parameters = CIRCUIT_INFO['4-2-2-25'].parameter;
|
|
@@ -114,7 +114,7 @@ export function getContractParams(
|
|
|
114
114
|
plonkProcessVkey = CIRCUIT_INFO['4-2-2-25']['plonk']?.process_vkey;
|
|
115
115
|
plonkTallyVkey = CIRCUIT_INFO['4-2-2-25']['plonk']?.tally_vkey;
|
|
116
116
|
}
|
|
117
|
-
} else if (
|
|
117
|
+
} else if (maxVoter <= 15625 && maxOption <= 125) {
|
|
118
118
|
// state_tree_depth: 6
|
|
119
119
|
// vote_option_tree_depth: 3
|
|
120
120
|
parameters = CIRCUIT_INFO['6-3-3-125'].parameter;
|
|
@@ -125,7 +125,7 @@ export function getContractParams(
|
|
|
125
125
|
plonkProcessVkey = CIRCUIT_INFO['6-3-3-125']['plonk']?.process_vkey;
|
|
126
126
|
plonkTallyVkey = CIRCUIT_INFO['6-3-3-125']['plonk']?.tally_vkey;
|
|
127
127
|
}
|
|
128
|
-
} else if (
|
|
128
|
+
} else if (maxVoter <= 1953125 && maxOption <= 125) {
|
|
129
129
|
// state_tree_depth: 9
|
|
130
130
|
// vote_option_tree_depth: 3
|
|
131
131
|
parameters = CIRCUIT_INFO['9-4-3-625'].parameter;
|
|
@@ -185,3 +185,23 @@ export function getContractParams(
|
|
|
185
185
|
};
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
|
+
|
|
189
|
+
export function getAMaciRoundCircuitFee(maxVoter: number, maxOption: number) {
|
|
190
|
+
let requiredFee = {
|
|
191
|
+
denom: 'peaka',
|
|
192
|
+
amount: '0',
|
|
193
|
+
};
|
|
194
|
+
if (maxVoter <= 25 && maxOption <= 5) {
|
|
195
|
+
// state_tree_depth: 2
|
|
196
|
+
// vote_option_tree_depth: 1
|
|
197
|
+
requiredFee.amount = '50000000000000000000';
|
|
198
|
+
} else if (maxVoter <= 625 && maxOption <= 25) {
|
|
199
|
+
// state_tree_depth: 4
|
|
200
|
+
// vote_option_tree_depth: 2
|
|
201
|
+
requiredFee.amount = '100000000000000000000';
|
|
202
|
+
} else {
|
|
203
|
+
throw new Error('Number of voters or options is too large.');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return requiredFee;
|
|
207
|
+
}
|
package/src/libs/errors/types.ts
CHANGED
|
@@ -3,6 +3,9 @@ export const ERROR = {
|
|
|
3
3
|
ERROR_CIRCUIT_NOT_FOUND: 'ERROR_CIRCUIT_NOT_FOUND',
|
|
4
4
|
ERROR_OPERATOR_INVALID_ADDRESS: 'ERROR_OPERATOR_INVALID_ADDRESS',
|
|
5
5
|
ERROR_OPERATOR_NOT_FOUND: 'ERROR_OPERATOR_NOT_FOUND',
|
|
6
|
+
ERROR_OPERATOR_DELAY_HISTORY_NOT_FOUND:
|
|
7
|
+
'ERROR_OPERATOR_DELAY_HISTORY_NOT_FOUND',
|
|
8
|
+
ERROR_QUERY_MISS_RATE_FAILED: 'ERROR_QUERY_MISS_RATE_FAILED',
|
|
6
9
|
ERROR_OPERATORS_NOT_FOUND: 'ERROR_OPERATORS_NOT_FOUND',
|
|
7
10
|
ERROR_PROOF_NOT_FOUND: 'ERROR_PROOF_NOT_FOUND',
|
|
8
11
|
ERROR_ROUND_INVALID_ADDRESS: 'ERROR_ROUND_INVALID_ADDRESS',
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
CircuitsResponse,
|
|
11
11
|
ProofResponse,
|
|
12
12
|
SignUpEventsResponse,
|
|
13
|
+
OperatorDelayOperationsResponse,
|
|
14
|
+
MissRateResponse,
|
|
13
15
|
} from '../../types';
|
|
14
16
|
import { IndexerParams } from './types';
|
|
15
17
|
import { Http } from '../http';
|
|
@@ -155,6 +157,25 @@ export class Indexer {
|
|
|
155
157
|
return await this.operator.getOperatorByAddress(address);
|
|
156
158
|
}
|
|
157
159
|
|
|
160
|
+
async getOperatorDelayOperationsByAddress(
|
|
161
|
+
address: string,
|
|
162
|
+
after: string,
|
|
163
|
+
limit?: number
|
|
164
|
+
): Promise<OperatorDelayOperationsResponse> {
|
|
165
|
+
return await this.operator.getOperatorDelayOperationsByAddress(
|
|
166
|
+
address,
|
|
167
|
+
after,
|
|
168
|
+
limit
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async queryMissRate(
|
|
173
|
+
address: string,
|
|
174
|
+
durationDay: number
|
|
175
|
+
): Promise<MissRateResponse> {
|
|
176
|
+
return await this.operator.queryMissRate(address, durationDay);
|
|
177
|
+
}
|
|
178
|
+
|
|
158
179
|
/**
|
|
159
180
|
* @method getOperators
|
|
160
181
|
* @description Get multiple operators.
|
package/src/libs/maci/maci.ts
CHANGED
|
@@ -253,6 +253,66 @@ export class MACI {
|
|
|
253
253
|
return circuitType === '1';
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
+
async queryRoundClaimable({
|
|
257
|
+
contractAddress,
|
|
258
|
+
}: {
|
|
259
|
+
contractAddress: string;
|
|
260
|
+
}): Promise<{
|
|
261
|
+
claimable: boolean | null;
|
|
262
|
+
balance: string | null;
|
|
263
|
+
}> {
|
|
264
|
+
try {
|
|
265
|
+
const roundInfo = await this.getRoundInfo({ contractAddress });
|
|
266
|
+
|
|
267
|
+
if (roundInfo.maciType !== 'aMACI') {
|
|
268
|
+
return {
|
|
269
|
+
claimable: null,
|
|
270
|
+
balance: null,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const votingEndTime = new Date(Number(roundInfo.votingEnd) / 10 ** 6);
|
|
275
|
+
const currentTime = new Date();
|
|
276
|
+
const threeDaysInMs = 3 * 24 * 60 * 60 * 1000;
|
|
277
|
+
|
|
278
|
+
if (currentTime.getTime() - votingEndTime.getTime() <= threeDaysInMs) {
|
|
279
|
+
return {
|
|
280
|
+
claimable: null,
|
|
281
|
+
balance: null,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const roundBalance = await this.indexer.balanceOf(contractAddress);
|
|
286
|
+
if (isErrorResponse(roundBalance)) {
|
|
287
|
+
throw new Error(
|
|
288
|
+
`Failed to query round balance: ${roundBalance.error.type} ${roundBalance.error.message}`
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (
|
|
293
|
+
roundBalance.data.balance &&
|
|
294
|
+
roundBalance.data.balance !== '0' &&
|
|
295
|
+
roundBalance.data.balance !== ''
|
|
296
|
+
) {
|
|
297
|
+
return {
|
|
298
|
+
claimable: true,
|
|
299
|
+
balance: roundBalance.data.balance,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
claimable: false,
|
|
305
|
+
balance: roundBalance.data.balance,
|
|
306
|
+
};
|
|
307
|
+
} catch (error) {
|
|
308
|
+
console.error('Error in queryRoundClaimable:', error);
|
|
309
|
+
return {
|
|
310
|
+
claimable: null,
|
|
311
|
+
balance: null,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
256
316
|
async queryRoundGasStation({ contractAddress }: { contractAddress: string }) {
|
|
257
317
|
const roundInfo = await this.getRoundInfo({ contractAddress });
|
|
258
318
|
|
|
@@ -5,6 +5,12 @@ import {
|
|
|
5
5
|
RoundsCountGraphqlResponse,
|
|
6
6
|
OperatorResponse,
|
|
7
7
|
OperatorsResponse,
|
|
8
|
+
OperatorDelayOperationsResponse,
|
|
9
|
+
OperatorDelayOperationsGraphqlResponse,
|
|
10
|
+
MissRateResponse,
|
|
11
|
+
MissRateType,
|
|
12
|
+
TransactionsGraphqlResponse,
|
|
13
|
+
RoundsGraphqlResponse,
|
|
8
14
|
} from '../../types';
|
|
9
15
|
import { isValidAddress } from '../../utils';
|
|
10
16
|
import { handleError, ErrorType } from '../errors';
|
|
@@ -131,6 +137,79 @@ export class Operator {
|
|
|
131
137
|
}
|
|
132
138
|
}
|
|
133
139
|
|
|
140
|
+
async getOperatorDelayOperationsByAddress(
|
|
141
|
+
address: string,
|
|
142
|
+
after: string,
|
|
143
|
+
limit?: number
|
|
144
|
+
): Promise<OperatorDelayOperationsResponse> {
|
|
145
|
+
try {
|
|
146
|
+
if (!isValidAddress(address)) {
|
|
147
|
+
return {
|
|
148
|
+
code: 400,
|
|
149
|
+
error: {
|
|
150
|
+
message: 'Invalid operator address format',
|
|
151
|
+
type: ERROR.ERROR_OPERATOR_INVALID_ADDRESS,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const OPERATORS_QUERY = `query ($limit: Int, $after: Cursor) {
|
|
157
|
+
operatorDelayOperations(first: $limit, after: $after, filter: {operatorAddress: {equalTo: "${address}"}}, orderBy: [TIMESTAMP_DESC]) {
|
|
158
|
+
pageInfo {
|
|
159
|
+
endCursor
|
|
160
|
+
hasNextPage
|
|
161
|
+
}
|
|
162
|
+
totalCount
|
|
163
|
+
edges {
|
|
164
|
+
cursor
|
|
165
|
+
node {
|
|
166
|
+
blockHeight
|
|
167
|
+
delayProcessDmsgCount
|
|
168
|
+
delayDuration
|
|
169
|
+
delayReason
|
|
170
|
+
delayType
|
|
171
|
+
id
|
|
172
|
+
nodeId
|
|
173
|
+
operatorAddress
|
|
174
|
+
timestamp
|
|
175
|
+
roundAddress
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}`;
|
|
180
|
+
|
|
181
|
+
const response =
|
|
182
|
+
await this.http.fetchGraphql<OperatorDelayOperationsGraphqlResponse>(
|
|
183
|
+
OPERATORS_QUERY,
|
|
184
|
+
after,
|
|
185
|
+
limit
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
if (
|
|
189
|
+
!response ||
|
|
190
|
+
!response.data ||
|
|
191
|
+
!response.data.operatorDelayOperations ||
|
|
192
|
+
!response.data.operatorDelayOperations.edges ||
|
|
193
|
+
response.data.operatorDelayOperations.edges.length === 0
|
|
194
|
+
) {
|
|
195
|
+
return {
|
|
196
|
+
code: 404,
|
|
197
|
+
error: {
|
|
198
|
+
message: `No operatorDelayOperations found for address ${address}`,
|
|
199
|
+
type: ERROR.ERROR_OPERATOR_DELAY_HISTORY_NOT_FOUND,
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
const operator: OperatorDelayOperationsResponse = {
|
|
204
|
+
code: 200,
|
|
205
|
+
data: response.data,
|
|
206
|
+
};
|
|
207
|
+
return operator;
|
|
208
|
+
} catch (error) {
|
|
209
|
+
return handleError(error as ErrorType);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
134
213
|
async getOperators(
|
|
135
214
|
after: string,
|
|
136
215
|
limit?: number
|
|
@@ -260,4 +339,263 @@ export class Operator {
|
|
|
260
339
|
return handleError(error as ErrorType);
|
|
261
340
|
}
|
|
262
341
|
}
|
|
342
|
+
|
|
343
|
+
async queryMissRate(
|
|
344
|
+
address: string,
|
|
345
|
+
durationDay: number
|
|
346
|
+
): Promise<MissRateResponse> {
|
|
347
|
+
try {
|
|
348
|
+
const now = new Date();
|
|
349
|
+
const startTime = new Date(
|
|
350
|
+
now.getTime() - durationDay * 24 * 60 * 60 * 1000
|
|
351
|
+
);
|
|
352
|
+
const startTimestamp = Math.floor(startTime.getTime() / 1000);
|
|
353
|
+
const endNanosTimestamp = Math.floor(startTime.getTime() * 1000000);
|
|
354
|
+
const txTimestamp = Math.floor(startTime.getTime());
|
|
355
|
+
|
|
356
|
+
const QUERY = `query ($limit: Int, $after: Cursor) {
|
|
357
|
+
operatorDelayOperations(
|
|
358
|
+
first: $limit,
|
|
359
|
+
after: $after,
|
|
360
|
+
filter: {
|
|
361
|
+
operatorAddress: {equalTo: "${address}"},
|
|
362
|
+
timestamp: { greaterThanOrEqualTo: "${startTimestamp}" }
|
|
363
|
+
},
|
|
364
|
+
orderBy: [TIMESTAMP_DESC]
|
|
365
|
+
) {
|
|
366
|
+
edges {
|
|
367
|
+
node {
|
|
368
|
+
blockHeight
|
|
369
|
+
delayProcessDmsgCount
|
|
370
|
+
delayDuration
|
|
371
|
+
delayReason
|
|
372
|
+
delayType
|
|
373
|
+
id
|
|
374
|
+
nodeId
|
|
375
|
+
operatorAddress
|
|
376
|
+
timestamp
|
|
377
|
+
roundAddress
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}`;
|
|
382
|
+
|
|
383
|
+
const ROUNDS_QUERY = `query ($limit: Int, $after: Cursor) {
|
|
384
|
+
rounds(first: $limit, after: $after,
|
|
385
|
+
filter: {
|
|
386
|
+
operator: {equalTo: "${address}"},
|
|
387
|
+
votingEnd: { greaterThanOrEqualTo: "${endNanosTimestamp}" }
|
|
388
|
+
},
|
|
389
|
+
orderBy: [TIMESTAMP_DESC]
|
|
390
|
+
){
|
|
391
|
+
pageInfo {
|
|
392
|
+
endCursor
|
|
393
|
+
hasNextPage
|
|
394
|
+
}
|
|
395
|
+
totalCount
|
|
396
|
+
edges {
|
|
397
|
+
node {
|
|
398
|
+
id
|
|
399
|
+
blockHeight
|
|
400
|
+
txHash
|
|
401
|
+
caller
|
|
402
|
+
admin
|
|
403
|
+
operator
|
|
404
|
+
contractAddress
|
|
405
|
+
circuitName
|
|
406
|
+
timestamp
|
|
407
|
+
votingStart
|
|
408
|
+
votingEnd
|
|
409
|
+
status
|
|
410
|
+
period
|
|
411
|
+
actionType
|
|
412
|
+
roundTitle
|
|
413
|
+
roundDescription
|
|
414
|
+
roundLink
|
|
415
|
+
coordinatorPubkeyX
|
|
416
|
+
coordinatorPubkeyY
|
|
417
|
+
voteOptionMap
|
|
418
|
+
results
|
|
419
|
+
allResult
|
|
420
|
+
gasStationEnable
|
|
421
|
+
totalGrant
|
|
422
|
+
baseGrant
|
|
423
|
+
totalBond
|
|
424
|
+
circuitType
|
|
425
|
+
circuitPower
|
|
426
|
+
certificationSystem
|
|
427
|
+
codeId
|
|
428
|
+
maciType
|
|
429
|
+
voiceCreditAmount
|
|
430
|
+
preDeactivateRoot
|
|
431
|
+
}
|
|
432
|
+
cursor
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
`;
|
|
437
|
+
|
|
438
|
+
const roundsResponse =
|
|
439
|
+
await this.http.fetchGraphql<RoundsGraphqlResponse>(
|
|
440
|
+
ROUNDS_QUERY,
|
|
441
|
+
'',
|
|
442
|
+
9999
|
|
443
|
+
);
|
|
444
|
+
const roundContractAddresses =
|
|
445
|
+
roundsResponse?.data?.rounds?.edges?.map(
|
|
446
|
+
(edge) => edge.node.contractAddress
|
|
447
|
+
) || [];
|
|
448
|
+
|
|
449
|
+
const TRANSACTIONS_QUERY = `query transactions($limit: Int, $after: Cursor) {
|
|
450
|
+
transactions(first: $limit, after: $after,
|
|
451
|
+
filter: {
|
|
452
|
+
timestamp: { greaterThanOrEqualTo: "${txTimestamp}" },
|
|
453
|
+
type: { equalTo: "op:procDeactivate" },
|
|
454
|
+
contractAddress: { in: ${JSON.stringify(roundContractAddresses)} }
|
|
455
|
+
},
|
|
456
|
+
orderBy: [TIMESTAMP_DESC]
|
|
457
|
+
){
|
|
458
|
+
pageInfo {
|
|
459
|
+
endCursor
|
|
460
|
+
hasNextPage
|
|
461
|
+
}
|
|
462
|
+
totalCount
|
|
463
|
+
edges {
|
|
464
|
+
cursor
|
|
465
|
+
node {
|
|
466
|
+
id
|
|
467
|
+
blockHeight
|
|
468
|
+
txHash
|
|
469
|
+
timestamp
|
|
470
|
+
type
|
|
471
|
+
status
|
|
472
|
+
circuitName
|
|
473
|
+
fee
|
|
474
|
+
gasUsed
|
|
475
|
+
gasWanted
|
|
476
|
+
caller
|
|
477
|
+
contractAddress
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}`;
|
|
482
|
+
|
|
483
|
+
const [delayResponse, transactionsResponse] = await Promise.all([
|
|
484
|
+
this.http.fetchGraphql<OperatorDelayOperationsGraphqlResponse>(
|
|
485
|
+
QUERY,
|
|
486
|
+
'',
|
|
487
|
+
9999
|
|
488
|
+
),
|
|
489
|
+
this.http.fetchGraphql<TransactionsGraphqlResponse>(
|
|
490
|
+
TRANSACTIONS_QUERY,
|
|
491
|
+
'',
|
|
492
|
+
9999
|
|
493
|
+
),
|
|
494
|
+
]);
|
|
495
|
+
|
|
496
|
+
const dailyStats = new Map<string, MissRateType>();
|
|
497
|
+
|
|
498
|
+
const endDate = new Date();
|
|
499
|
+
for (let i = 0; i < durationDay; i++) {
|
|
500
|
+
const date = new Date(endDate.getTime() - i * 24 * 60 * 60 * 1000)
|
|
501
|
+
.toISOString()
|
|
502
|
+
.split('T')[0];
|
|
503
|
+
dailyStats.set(date, {
|
|
504
|
+
delayCount: 0,
|
|
505
|
+
deactivateDelay: {
|
|
506
|
+
count: 0,
|
|
507
|
+
dmsgCount: 0,
|
|
508
|
+
},
|
|
509
|
+
tallyDelay: {
|
|
510
|
+
count: 0,
|
|
511
|
+
},
|
|
512
|
+
totalDelayDuration: 0,
|
|
513
|
+
avgDelayDuration: 0,
|
|
514
|
+
tallyCount: 0,
|
|
515
|
+
deactivateCount: 0,
|
|
516
|
+
missRate: 0,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
delayResponse.data.operatorDelayOperations.edges.forEach(({ node }) => {
|
|
521
|
+
const date = new Date(parseInt(node.timestamp) * 1000)
|
|
522
|
+
.toISOString()
|
|
523
|
+
.split('T')[0];
|
|
524
|
+
|
|
525
|
+
if (dailyStats.has(date)) {
|
|
526
|
+
const stats = dailyStats.get(date)!;
|
|
527
|
+
stats.delayCount++;
|
|
528
|
+
stats.totalDelayDuration += parseInt(node.delayDuration);
|
|
529
|
+
if (node.delayType === 'deactivate_delay') {
|
|
530
|
+
stats.deactivateDelay.count++;
|
|
531
|
+
stats.deactivateDelay.dmsgCount += node.delayProcessDmsgCount;
|
|
532
|
+
} else if (node.delayType === 'tally_delay') {
|
|
533
|
+
stats.tallyDelay.count++;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
if (roundsResponse?.data?.rounds?.edges) {
|
|
539
|
+
roundsResponse.data.rounds.edges.forEach(({ node }) => {
|
|
540
|
+
const date = new Date(parseInt(node.votingEnd) / 1000000)
|
|
541
|
+
.toISOString()
|
|
542
|
+
.split('T')[0];
|
|
543
|
+
if (dailyStats.has(date)) {
|
|
544
|
+
const stats = dailyStats.get(date)!;
|
|
545
|
+
stats.tallyCount++;
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (transactionsResponse?.data?.transactions?.edges) {
|
|
551
|
+
transactionsResponse.data.transactions.edges.forEach(({ node }) => {
|
|
552
|
+
const date = new Date(parseInt(node.timestamp))
|
|
553
|
+
.toISOString()
|
|
554
|
+
.split('T')[0];
|
|
555
|
+
if (dailyStats.has(date)) {
|
|
556
|
+
const stats = dailyStats.get(date)!;
|
|
557
|
+
stats.deactivateCount++;
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return {
|
|
563
|
+
code: 200,
|
|
564
|
+
data: {
|
|
565
|
+
missRate: Array.from(dailyStats.entries())
|
|
566
|
+
.map(([date, stats]) => ({
|
|
567
|
+
date,
|
|
568
|
+
delayCount: stats.delayCount,
|
|
569
|
+
deactivateDelay: stats.deactivateDelay,
|
|
570
|
+
tallyDelay: stats.tallyDelay,
|
|
571
|
+
totalDelayDuration: stats.totalDelayDuration,
|
|
572
|
+
avgDelayDuration:
|
|
573
|
+
stats.delayCount > 0
|
|
574
|
+
? stats.totalDelayDuration / stats.delayCount
|
|
575
|
+
: 0,
|
|
576
|
+
tallyCount: stats.tallyCount,
|
|
577
|
+
deactivateCount: stats.deactivateCount,
|
|
578
|
+
missRate:
|
|
579
|
+
stats.deactivateCount + stats.tallyCount > 0
|
|
580
|
+
? parseFloat(
|
|
581
|
+
(
|
|
582
|
+
(stats.deactivateDelay.count + stats.tallyDelay.count) /
|
|
583
|
+
(stats.deactivateCount + stats.tallyCount)
|
|
584
|
+
).toFixed(2)
|
|
585
|
+
)
|
|
586
|
+
: 0,
|
|
587
|
+
}))
|
|
588
|
+
.sort((a, b) => b.date.localeCompare(a.date)),
|
|
589
|
+
},
|
|
590
|
+
};
|
|
591
|
+
} catch (error) {
|
|
592
|
+
return {
|
|
593
|
+
code: 404,
|
|
594
|
+
error: {
|
|
595
|
+
message: 'Query miss rate failed',
|
|
596
|
+
type: ERROR.ERROR_QUERY_MISS_RATE_FAILED,
|
|
597
|
+
},
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
}
|
|
263
601
|
}
|