Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"scripts": {
"build": "tsc",
"typecheck": "tsc --noEmit",
"test": "bash scripts/run-tests.sh",
"start": "node ./app/index.js",
"run": "node ./app/index.js",
"eslint": "eslint . --ext ts --fix"
Expand Down
62 changes: 62 additions & 0 deletions scripts/run-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
#
# Runs every script under test/ as a plain Node process and reports a PASS/FAIL
# summary. The tests are top-level `assert` scripts (no test framework), so the
# contract is simply: exit 0 means pass.
#
# Most tests require("../app/...") — the compiled output — so the tree is built
# first. Set SKIP_BUILD=1 to reuse an existing app/ build.
#
set -u

cd "$(dirname "$0")/.."

if [ "${SKIP_BUILD:-0}" != "1" ]; then
echo "==> npm run build"
if ! npm run build; then
echo "==> build failed, not running tests" >&2
exit 1
fi
echo
fi

shopt -s nullglob
tests=(test/*.js)
shopt -u nullglob

if [ ${#tests[@]} -eq 0 ]; then
echo "==> no test files found under test/" >&2
exit 1
fi

log_dir="$(mktemp -d)"
trap 'rm -rf "$log_dir"' EXIT

passed=0
failed=0
failed_names=()

echo "==> running ${#tests[@]} test(s)"
for test_file in "${tests[@]}"; do
log_file="$log_dir/$(basename "$test_file").log"
if node "$test_file" >"$log_file" 2>&1; then
echo "PASS $test_file"
passed=$((passed + 1))
else
echo "FAIL $test_file"
failed=$((failed + 1))
failed_names+=("$test_file")
# Only failures print their output, so a green run stays readable.
sed 's/^/ | /' "$log_file"
fi
done

echo
echo "==> ${passed} passed, ${failed} failed, ${#tests[@]} total"

if [ "$failed" -ne 0 ]; then
for name in "${failed_names[@]}"; do
echo "==> failed: $name" >&2
done
exit 1
fi
17 changes: 12 additions & 5 deletions src/chain/sol/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const SIGNATURE_PAGE_LIMIT = 100;
const SOLANA_COMMITMENT = "finalized";
const SOLANA_RETRY_ATTEMPTS = 3;
const SOLANA_RETRY_DELAY_MS = 1000;
const SOLANA_GET_TRANSACTION_RETRY_DELAYS_MS = [10000, 30000, 60000];

export class SolChain {
cfg: Chain;
Expand All @@ -22,6 +23,8 @@ export class SolChain {
mcsAddresses: string[];
retryAttempts: number;
retryDelayMs: number;
getTransactionRetryDelaysMs: number[];
sleep: (ms: number) => Promise<void>;

constructor(cfg: Chain, butter: string, butterApiKey: string) {
this.cfg = cfg
Expand All @@ -32,6 +35,8 @@ export class SolChain {
))
this.retryAttempts = SOLANA_RETRY_ATTEMPTS
this.retryDelayMs = SOLANA_RETRY_DELAY_MS
this.getTransactionRetryDelaysMs = SOLANA_GET_TRANSACTION_RETRY_DELAYS_MS
this.sleep = delay
this.parser = new SolEventParser({ eventProgramIds: this.mcsAddresses })
this.handler = new SolEventHandler(cfg, butter, butterApiKey)
}
Expand All @@ -51,7 +56,6 @@ export class SolChain {
console.log("solana catch err", err)
await delay(3000)
} finally {
console.log("solana filter is running")
await delay(3000)
}
}
Expand Down Expand Up @@ -136,7 +140,7 @@ export class SolChain {
throw new Error("solana getTransaction returned null")
}
return trx
}, () => true)
}, () => true, this.getTransactionRetryDelaysMs)
}

private async handleTransactionWithRetry(
Expand All @@ -155,18 +159,21 @@ export class SolChain {
txHash: string,
operation: () => Promise<T>,
shouldRetry: (error: Error) => boolean,
retryDelaysMs?: number[],
): Promise<T> {
let lastError: Error | undefined
for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
const maxAttempts = retryDelaysMs ? retryDelaysMs.length + 1 : this.retryAttempts

for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await operation()
} catch (err) {
lastError = toError(err)
if (attempt >= this.retryAttempts || !shouldRetry(lastError)) {
if (attempt >= maxAttempts || !shouldRetry(lastError)) {
break
}
console.log("solana retry", stage, "txHash", txHash, "attempt", attempt, "err", lastError.message)
await delay(this.retryDelayMs)
await this.sleep(retryDelaysMs ? retryDelaysMs[attempt - 1] : this.retryDelayMs)
}
}
throw lastError || new Error(`${stage} failed`)
Expand Down
4 changes: 2 additions & 2 deletions src/utils/time.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export function delay(ms: number) {
export function delay(ms: number): Promise<void> {
return new Promise( resolve => setTimeout(resolve, ms) );
}
}
Loading