Skip to content

Commit

Permalink
Add caching of startBlock
Browse files Browse the repository at this point in the history
  • Loading branch information
varanauskas authored Oct 10, 2023
1 parent a464dcd commit cb268ee
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 8 deletions.
36 changes: 33 additions & 3 deletions lib/etherscan.mjs
Original file line number Diff line number Diff line change
@@ -1,23 +1,53 @@
import { RetryableError, wrapRetryable } from "./retryable.mjs";

/**
* Cache startBlocks
* @type {Map<string, BigInt>}
*/
let startBlocks = new Map();

export const getTokenTx = wrapRetryable(async function getTokenTx(etherscan, params) {
// Construct URL without startBlock
const url = new URL(`https://api.${etherscan}/api`);
url.searchParams.append("module", "account");
url.searchParams.append("action", "tokentx");
url.searchParams.append("page", "1");
Object.entries(params).forEach(([key, value]) => url.searchParams.append(key.toLowerCase(), value));
url.searchParams.append("sort", "desc");

// Calculate Cache key before appending startBlock
const key = url.toString();

// Append startBlock from cache if present
const startBlock = startBlocks.get(key);
if (startBlock) url.searchParams.append("startblock", startBlock.toString());

let response;
try {
response = await fetch(url);
} catch (error) {
throw new RetryableError(`${etherscan} connection error`, true, error);
}
if (!response.ok)
throw new RetryableError(`${etherscan} got ${response.status}`, response.status >= 500);
throw new RetryableError(
`${etherscan} got ${response.status}`,
response.status >= 500,
url
);
const text = await response.text();
const json = JSON.parse(text);
if (json.status !== "1")
throw new RetryableError(`${etherscan} error "${text}}"`, json.result === "Invalid API Key" || json.message === "No transactions found");
return json.result;
throw new RetryableError(
`${etherscan} error "${text}}"`,
json.result === "Invalid API Key" || json.message === "No transactions found",
url
);
const { result } = json;

// If transactions present cache last transaction start block
if (result.length !== 0) {
result.sort((a, b) => Number(BigInt(a.blockNumber) - BigInt(b.blockNumber)));
startBlocks.set(key, BigInt(result.at(-1).blockNumber) - 1n);
}
return result;
});
8 changes: 4 additions & 4 deletions lib/retryable.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { sleep } from "./utils.mjs";

export class RetryableError extends Error {
#canRetryOrRetryAfter;
originalError;
details;

constructor(message, canRetryOrRetryAfter, originalError) {
constructor(message, canRetryOrRetryAfter, details) {
super(message);
this.#canRetryOrRetryAfter = canRetryOrRetryAfter;
this.originalError = originalError;
this.details = details;
}

get retryAfter() {
Expand All @@ -29,7 +29,7 @@ async function callRetryable(fn, attempt = 0) {
} catch (error) {
if (error instanceof RetryableError && error.canRetry && attempt <= 10) {
const retryAfter = error.retryAfter ?? (2 ** attempt) * 750;
console.warn(`${error.message}, retrying after ${retryAfter} ms`, error.originalError);
console.warn(`${error.message}, retrying after ${retryAfter} ms`, error.details);
await sleep(retryAfter);
return callRetryable(fn, attempt++);
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tokentx-notifier",
"version": "1.4.4",
"version": "1.4.5",
"description": "A telegram bot that sends a message when new ERC20 transactions are detected",
"keywords": ["telegram", "crypto", "ethereum", "erc20", "bot"],
"homepage": "https://github.com/varanauskas/tokentx-notifier",
Expand Down

0 comments on commit cb268ee

Please sign in to comment.