@certik/skynet 0.7.10 → 0.7.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@certik/skynet",
3
- "version": "0.7.10",
3
+ "version": "0.7.14",
4
4
  "description": "Skynet Shared JS library",
5
5
  "main": "index.js",
6
6
  "author": "CertiK Engineering",
@@ -28,4 +28,4 @@
28
28
  "sinon": "^11.1.2"
29
29
  },
30
30
  "license": "MIT"
31
- }
31
+ }
package/price.js CHANGED
@@ -1,48 +1,48 @@
1
- const { getDocClient } = require("./dynamodb");
2
-
3
- async function queryPrice(tableName, address, timestamp, op = "<") {
4
- const docClient = getDocClient();
5
-
6
- return await docClient
7
- .query({
8
- TableName: tableName,
9
- KeyConditionExpression: `#address = :address and #timestamp ${op} :timestamp`,
10
- ExpressionAttributeNames: {
11
- "#timestamp": "timestamp",
12
- "#address": "address"
13
- },
14
- ExpressionAttributeValues: {
15
- ":timestamp": timestamp,
16
- ":address": address
17
- },
18
- Limit: 1,
19
- ScanIndexForward: false
20
- })
21
- .promise();
22
- }
23
-
24
- async function getPricePreviousRecord(tableName, address, timestamp) {
25
- const query = await queryPrice(tableName, address, timestamp, "<");
26
-
27
- if (query.Count === 0) {
28
- return null;
29
- } else {
30
- return query.Items[0];
31
- }
32
- }
33
-
34
- async function getPriceClosestTo(tableName, address, timestamp) {
35
- const query = await queryPrice(tableName, address, timestamp, "<=");
36
-
37
- if (query.Count === 0) {
38
- return 0;
39
- } else {
40
- const { price } = query.Items[0];
41
- return price;
42
- }
43
- }
44
-
45
- module.exports = {
46
- getPriceClosestTo,
47
- getPricePreviousRecord
48
- };
1
+ const { getDocClient } = require("./dynamodb");
2
+
3
+ async function queryPrice(tableName, address, timestamp, op = "<") {
4
+ const docClient = getDocClient();
5
+
6
+ return await docClient
7
+ .query({
8
+ TableName: tableName,
9
+ KeyConditionExpression: `#address = :address and #timestamp ${op} :timestamp`,
10
+ ExpressionAttributeNames: {
11
+ "#timestamp": "timestamp",
12
+ "#address": "address"
13
+ },
14
+ ExpressionAttributeValues: {
15
+ ":timestamp": timestamp,
16
+ ":address": address
17
+ },
18
+ Limit: 1,
19
+ ScanIndexForward: false
20
+ })
21
+ .promise();
22
+ }
23
+
24
+ async function getPricePreviousRecord(tableName, address, timestamp) {
25
+ const query = await queryPrice(tableName, address, timestamp, "<");
26
+
27
+ if (query.Count === 0) {
28
+ return null;
29
+ } else {
30
+ return query.Items[0];
31
+ }
32
+ }
33
+
34
+ async function getPriceClosestTo(tableName, address, timestamp) {
35
+ const query = await queryPrice(tableName, address, timestamp, "<=");
36
+
37
+ if (query.Count === 0) {
38
+ return 0;
39
+ } else {
40
+ const { price } = query.Items[0];
41
+ return price;
42
+ }
43
+ }
44
+
45
+ module.exports = {
46
+ getPriceClosestTo,
47
+ getPricePreviousRecord
48
+ };
package/primitive.js CHANGED
@@ -1,77 +1,77 @@
1
- async function getOverrides(docClient, projectId, address, log, primitive) {
2
- try {
3
- console.log("ProjectId:" + projectId + " address:" + address);
4
-
5
- const whitelist = [];
6
- const queryProjectOverrides = await docClient
7
- .get({
8
- TableName: "skynet-prd-primitive-manual-overrides",
9
- Key: {
10
- primitive: primitive,
11
- overrideId: "project/" + projectId,
12
- },
13
- })
14
- .promise();
15
- if (queryProjectOverrides.Item && queryProjectOverrides.Item.whitelist) {
16
- whitelist.push(...queryProjectOverrides.Item.whitelist);
17
- }
18
-
19
- const queryAddressOverrides = await docClient
20
- .get({
21
- TableName: "skynet-prd-primitive-manual-overrides",
22
- Key: {
23
- primitive: primitive,
24
- overrideId: "address/" + address,
25
- },
26
- })
27
- .promise();
28
-
29
- if (queryAddressOverrides.Item && queryAddressOverrides.Item.whitelist) {
30
- console.log(queryAddressOverrides.Item.whitelist);
31
- whitelist.push(...queryAddressOverrides.Item.whitelist);
32
- }
33
- if (whitelist.length === 0) {
34
- log(
35
- "ProjectId " + projectId + " ,Address " + address + " has no override"
36
- );
37
- }
38
- console.log("Whitelist:" + whitelist);
39
- return whitelist;
40
- } catch (scanFeedErr) {
41
- log("scan feed error, check dynamodb table", scanFeedErr);
42
- return null;
43
- }
44
- }
45
-
46
- async function overrideScoreAndIssues(
47
- docClient,
48
- projectId,
49
- address,
50
- score,
51
- issues,
52
- log,
53
- primitive
54
- ) {
55
- const overrides = await getOverrides(
56
- docClient,
57
- projectId,
58
- address,
59
- log,
60
- primitive
61
- );
62
-
63
- issues = issues.filter((issue) => {
64
- if (overrides.includes(issue.code)) {
65
- score += issue.reduction;
66
- return false;
67
- } else return true;
68
- });
69
- return {
70
- score,
71
- issues,
72
- };
73
- }
74
- module.exports = {
75
- getOverrides,
76
- overrideScoreAndIssues,
77
- };
1
+ async function getOverrides(docClient, projectId, address, log, primitive) {
2
+ try {
3
+ console.log("ProjectId:" + projectId + " address:" + address);
4
+
5
+ const whitelist = [];
6
+ const queryProjectOverrides = await docClient
7
+ .get({
8
+ TableName: "skynet-prd-primitive-manual-overrides",
9
+ Key: {
10
+ primitive: primitive,
11
+ overrideId: "project/" + projectId,
12
+ },
13
+ })
14
+ .promise();
15
+ if (queryProjectOverrides.Item && queryProjectOverrides.Item.whitelist) {
16
+ whitelist.push(...queryProjectOverrides.Item.whitelist);
17
+ }
18
+
19
+ const queryAddressOverrides = await docClient
20
+ .get({
21
+ TableName: "skynet-prd-primitive-manual-overrides",
22
+ Key: {
23
+ primitive: primitive,
24
+ overrideId: "address/" + address,
25
+ },
26
+ })
27
+ .promise();
28
+
29
+ if (queryAddressOverrides.Item && queryAddressOverrides.Item.whitelist) {
30
+ console.log(queryAddressOverrides.Item.whitelist);
31
+ whitelist.push(...queryAddressOverrides.Item.whitelist);
32
+ }
33
+ if (whitelist.length === 0) {
34
+ log(
35
+ "ProjectId " + projectId + " ,Address " + address + " has no override"
36
+ );
37
+ }
38
+ console.log("Whitelist:" + whitelist);
39
+ return whitelist;
40
+ } catch (scanFeedErr) {
41
+ log("scan feed error, check dynamodb table", scanFeedErr);
42
+ return null;
43
+ }
44
+ }
45
+
46
+ async function overrideScoreAndIssues(
47
+ docClient,
48
+ projectId,
49
+ address,
50
+ score,
51
+ issues,
52
+ log,
53
+ primitive
54
+ ) {
55
+ const overrides = await getOverrides(
56
+ docClient,
57
+ projectId,
58
+ address,
59
+ log,
60
+ primitive
61
+ );
62
+
63
+ issues = issues.filter((issue) => {
64
+ if (overrides.includes(issue.code)) {
65
+ score += issue.reduction;
66
+ return false;
67
+ } else return true;
68
+ });
69
+ return {
70
+ score,
71
+ issues,
72
+ };
73
+ }
74
+ module.exports = {
75
+ getOverrides,
76
+ overrideScoreAndIssues,
77
+ };
package/rateLimit.js CHANGED
@@ -1,21 +1,21 @@
1
- const fetch = require("node-fetch");
2
-
3
- /**
4
- * Record the api access and return the rate limit result.
5
- * @async
6
- * @param {string} name - The name of the key.
7
- * @param {number} unitTime - The sliding window for rate limiting, in seconds.
8
- * @param {number} maxAccess - The total count of allowed accesses in a sliding window.
9
- * @returns {Promise<boolean>} This request should be allowed or not.
10
- */
11
- exports.isRateLimited = async (name, unitTime, maxAccess) => {
12
- try {
13
- const url = `https://rate-limit.certik-skynet.com?name=${name}&unitTime=${unitTime}&maxAccess=${maxAccess}`;
14
- const response = await fetch(url);
15
- const result = await response.json();
16
- if (result.remaining > 0) return false;
17
- return true;
18
- } catch {
19
- return false;
20
- }
21
- };
1
+ const fetch = require("node-fetch");
2
+
3
+ /**
4
+ * Record the api access and return the rate limit result.
5
+ * @async
6
+ * @param {string} name - The name of the key.
7
+ * @param {number} unitTime - The sliding window for rate limiting, in seconds.
8
+ * @param {number} maxAccess - The total count of allowed accesses in a sliding window.
9
+ * @returns {Promise<boolean>} This request should be allowed or not.
10
+ */
11
+ exports.isRateLimited = async (name, unitTime, maxAccess) => {
12
+ try {
13
+ const url = `https://rate-limit.certik-skynet.com?name=${name}&unitTime=${unitTime}&maxAccess=${maxAccess}`;
14
+ const response = await fetch(url);
15
+ const result = await response.json();
16
+ if (result.remaining > 0) return false;
17
+ return true;
18
+ } catch {
19
+ return false;
20
+ }
21
+ };
package/s3.js CHANGED
@@ -1,93 +1,93 @@
1
- const {
2
- getAWSAccessKeyId,
3
- getAWSSecretAccessKey,
4
- getAWSRegion
5
- } = require("./env");
6
- const { S3 } = require("aws-sdk");
7
-
8
- function getS3() {
9
- return new S3({
10
- accessKeyId: getAWSAccessKeyId(),
11
- secretAccessKey: getAWSSecretAccessKey(),
12
- region: getAWSRegion()
13
- });
14
- }
15
-
16
- async function readFile(bucketName, key, verbose = false) {
17
- const s3 = getS3();
18
-
19
- const params = { Bucket: bucketName, Key: key };
20
-
21
- try {
22
- const result = await s3.getObject(params).promise();
23
- return result.Body.toString("utf-8");
24
- } catch (noSuchKeyErr) {
25
- if (verbose) {
26
- console.log("no such bucket or key", bucketName, key);
27
- }
28
- // do nothing
29
- return null;
30
- }
31
- }
32
-
33
- async function hasFile(bucketName, key) {
34
- const s3 = getS3();
35
-
36
- try {
37
- await s3.headObject({ Bucket: bucketName, Key: key }).promise();
38
-
39
- return true;
40
- } catch (headErr) {
41
- if (headErr.statusCode === 404) {
42
- return false;
43
- }
44
-
45
- throw headErr;
46
- }
47
- }
48
-
49
- async function writeFile(bucketName, key, body, options = {}) {
50
- const s3 = getS3();
51
-
52
- if (options.skipIfExists) {
53
- if (await hasFile(bucketName, key)) {
54
- return;
55
- }
56
- }
57
-
58
- const verbose = options.verbose || false;
59
-
60
- const params = { Bucket: bucketName, Key: key, Body: body };
61
-
62
- if (options.acl) {
63
- params.ACL = options.acl;
64
- }
65
-
66
- if (verbose) {
67
- console.log("uploading", key);
68
- }
69
-
70
- await s3.upload(params).promise();
71
- }
72
-
73
- async function deleteFile(bucketName, key, verbose = false) {
74
- const s3 = getS3();
75
-
76
- const params = { Bucket: bucketName, Key: key };
77
-
78
- try {
79
- await s3.deleteObject(params).promise();
80
- } catch (noSuchKeyErr) {
81
- if (verbose) {
82
- console.log("no such bucket or key", bucketName, key);
83
- }
84
- }
85
- }
86
-
87
- module.exports = {
88
- getS3,
89
- hasFile,
90
- readFile,
91
- writeFile,
92
- deleteFile
93
- };
1
+ const {
2
+ getAWSAccessKeyId,
3
+ getAWSSecretAccessKey,
4
+ getAWSRegion
5
+ } = require("./env");
6
+ const { S3 } = require("aws-sdk");
7
+
8
+ function getS3() {
9
+ return new S3({
10
+ accessKeyId: getAWSAccessKeyId(),
11
+ secretAccessKey: getAWSSecretAccessKey(),
12
+ region: getAWSRegion()
13
+ });
14
+ }
15
+
16
+ async function readFile(bucketName, key, verbose = false) {
17
+ const s3 = getS3();
18
+
19
+ const params = { Bucket: bucketName, Key: key };
20
+
21
+ try {
22
+ const result = await s3.getObject(params).promise();
23
+ return result.Body.toString("utf-8");
24
+ } catch (noSuchKeyErr) {
25
+ if (verbose) {
26
+ console.log("no such bucket or key", bucketName, key);
27
+ }
28
+ // do nothing
29
+ return null;
30
+ }
31
+ }
32
+
33
+ async function hasFile(bucketName, key) {
34
+ const s3 = getS3();
35
+
36
+ try {
37
+ await s3.headObject({ Bucket: bucketName, Key: key }).promise();
38
+
39
+ return true;
40
+ } catch (headErr) {
41
+ if (headErr.statusCode === 404) {
42
+ return false;
43
+ }
44
+
45
+ throw headErr;
46
+ }
47
+ }
48
+
49
+ async function writeFile(bucketName, key, body, options = {}) {
50
+ const s3 = getS3();
51
+
52
+ if (options.skipIfExists) {
53
+ if (await hasFile(bucketName, key)) {
54
+ return;
55
+ }
56
+ }
57
+
58
+ const verbose = options.verbose || false;
59
+
60
+ const params = { Bucket: bucketName, Key: key, Body: body };
61
+
62
+ if (options.acl) {
63
+ params.ACL = options.acl;
64
+ }
65
+
66
+ if (verbose) {
67
+ console.log("uploading", key);
68
+ }
69
+
70
+ await s3.upload(params).promise();
71
+ }
72
+
73
+ async function deleteFile(bucketName, key, verbose = false) {
74
+ const s3 = getS3();
75
+
76
+ const params = { Bucket: bucketName, Key: key };
77
+
78
+ try {
79
+ await s3.deleteObject(params).promise();
80
+ } catch (noSuchKeyErr) {
81
+ if (verbose) {
82
+ console.log("no such bucket or key", bucketName, key);
83
+ }
84
+ }
85
+ }
86
+
87
+ module.exports = {
88
+ getS3,
89
+ hasFile,
90
+ readFile,
91
+ writeFile,
92
+ deleteFile
93
+ };
package/scan.js CHANGED
@@ -1,68 +1,68 @@
1
- const { exponentialRetry, wait } = require("@certik/skynet/availability");
2
- const { PROTOCOLS } = require("@certik/skynet/const");
3
- const fetch = require("node-fetch");
4
-
5
- const BATCH_SIZE = 10_000; // Max number of transactions fetched from scan api at once
6
-
7
- function getScanApiEndpoint(protocol, addr, startBlock = 0, endBlock = null) {
8
- const { endpoint, key } = PROTOCOLS[protocol].scanApi;
9
- let url = `${endpoint}?module=account&action=txlist&address=${addr}&apikey=${key}&sort=asc&startblock=${startBlock}&page=1&offset=${BATCH_SIZE}`;
10
-
11
- if (endBlock) {
12
- url += `&endblock=${endBlock}`;
13
- }
14
-
15
- return url;
16
- }
17
-
18
- async function fetchTxs(protocol, addr, startBlock, to, verbose) {
19
- const url = getScanApiEndpoint(protocol, addr, startBlock, to);
20
-
21
- const txs = await exponentialRetry(
22
- async () => {
23
- const res = await fetch(url);
24
- const scanJSON = await res.json();
25
- return scanJSON.result;
26
- },
27
- { maxRetry: 6, test: Array.isArray, verbose }
28
- )
29
-
30
- console.log(`start block=${startBlock}, total items=${txs.length}`);
31
- return txs;
32
- }
33
-
34
- async function streamTxs(address, since, to, callback, verbose) {
35
- let startBlock = since;
36
- let hasMorePage = true;
37
- let lastTxHash = null;
38
-
39
- const [protocol, addr] = address.split(":");
40
-
41
- while (hasMorePage) {
42
- const txs = await fetchTxs(protocol, addr, startBlock, to, verbose);
43
-
44
- // This will only ever happen if the total number of txs is a multiple of our batch size
45
- if (txs.length === 0) {
46
- break;
47
- }
48
-
49
- // If our start block overlaps with the last request's end block, then we may have some overlapping txs
50
- // To avoid adding those txs to the db twice, we filter them out
51
- const newTxsStartIdx = txs.indexOf(({ hash }) => hash === lastTxHash);
52
-
53
- const newTxs = txs.slice(newTxsStartIdx !== -1 ? newTxsStartIdx : 0);
54
-
55
- for (const tx of newTxs) {
56
- callback(tx);
57
- }
58
-
59
- hasMorePage = txs.length === BATCH_SIZE;
60
-
61
- startBlock = parseInt(txs[txs.length - 1].blockNumber, 10) + 1;
62
- lastTxHash = txs[txs.length - 1].hash;
63
-
64
- await wait(2000);
65
- }
66
- }
67
-
1
+ const { exponentialRetry, wait } = require("@certik/skynet/availability");
2
+ const { PROTOCOLS } = require("@certik/skynet/const");
3
+ const fetch = require("node-fetch");
4
+
5
+ const BATCH_SIZE = 10_000; // Max number of transactions fetched from scan api at once
6
+
7
+ function getScanApiEndpoint(protocol, addr, startBlock = 0, endBlock = null) {
8
+ const { endpoint, key } = PROTOCOLS[protocol].scanApi;
9
+ let url = `${endpoint}?module=account&action=txlist&address=${addr}&apikey=${key}&sort=asc&startblock=${startBlock}&page=1&offset=${BATCH_SIZE}`;
10
+
11
+ if (endBlock) {
12
+ url += `&endblock=${endBlock}`;
13
+ }
14
+
15
+ return url;
16
+ }
17
+
18
+ async function fetchTxs(protocol, addr, startBlock, to, verbose) {
19
+ const url = getScanApiEndpoint(protocol, addr, startBlock, to);
20
+
21
+ const txs = await exponentialRetry(
22
+ async () => {
23
+ const res = await fetch(url);
24
+ const scanJSON = await res.json();
25
+ return scanJSON.result;
26
+ },
27
+ { maxRetry: 6, test: Array.isArray, verbose }
28
+ )
29
+
30
+ console.log(`start block=${startBlock}, total items=${txs.length}`);
31
+ return txs;
32
+ }
33
+
34
+ async function streamTxs(address, since, to, callback, verbose) {
35
+ let startBlock = since;
36
+ let hasMorePage = true;
37
+ let lastTxHash = null;
38
+
39
+ const [protocol, addr] = address.split(":");
40
+
41
+ while (hasMorePage) {
42
+ const txs = await fetchTxs(protocol, addr, startBlock, to, verbose);
43
+
44
+ // This will only ever happen if the total number of txs is a multiple of our batch size
45
+ if (txs.length === 0) {
46
+ break;
47
+ }
48
+
49
+ // If our start block overlaps with the last request's end block, then we may have some overlapping txs
50
+ // To avoid adding those txs to the db twice, we filter them out
51
+ const newTxsStartIdx = txs.indexOf(({ hash }) => hash === lastTxHash);
52
+
53
+ const newTxs = txs.slice(newTxsStartIdx !== -1 ? newTxsStartIdx : 0);
54
+
55
+ for (const tx of newTxs) {
56
+ callback(tx);
57
+ }
58
+
59
+ hasMorePage = txs.length === BATCH_SIZE;
60
+
61
+ startBlock = parseInt(txs[txs.length - 1].blockNumber, 10) + 1;
62
+ lastTxHash = txs[txs.length - 1].hash;
63
+
64
+ await wait(2000);
65
+ }
66
+ }
67
+
68
68
  module.exports = { streamTxs };
package/sqs.js CHANGED
@@ -1,12 +1,12 @@
1
- const { SQS } = require("aws-sdk");
2
- const { getAWSAccessKeyId, getAWSSecretAccessKey, getAWSRegion } = require("./env");
3
-
4
- function getSQS() {
5
- return new SQS({
6
- accessKeyId: getAWSAccessKeyId(),
7
- secretAccessKey: getAWSSecretAccessKey(),
8
- region: getAWSRegion(),
9
- });
10
- }
11
-
12
- module.exports = { getSQS };
1
+ const { SQS } = require("aws-sdk");
2
+ const { getAWSAccessKeyId, getAWSSecretAccessKey, getAWSRegion } = require("./env");
3
+
4
+ function getSQS() {
5
+ return new SQS({
6
+ accessKeyId: getAWSAccessKeyId(),
7
+ secretAccessKey: getAWSSecretAccessKey(),
8
+ region: getAWSRegion(),
9
+ });
10
+ }
11
+
12
+ module.exports = { getSQS };