This commit is contained in:
2026-05-30 01:14:30 +02:00
parent ffa4acbccb
commit 7bd6593015
20 changed files with 4359 additions and 456 deletions

View File

@@ -117,13 +117,49 @@ function isSolanaAddressLike(value: string): boolean {
return /^[1-9A-HJ-NP-Za-km-z]+$/.test(trimmed);
}
function splitSourceAddresses(value: string): string[] {
const seen = new Set<string>();
const addresses: string[] = [];
for (const token of value.split(/[\s,;]+/g)) {
const trimmed = token.trim();
if (trimmed === "" || seen.has(trimmed)) {
continue;
}
seen.add(trimmed);
addresses.push(trimmed);
}
return addresses;
}
function isSolanaSignatureLike(value: string): boolean {
const trimmed = value.trim();
if (trimmed.length < 64 || trimmed.length > 128) {
return false;
}
return /^[1-9A-HJ-NP-Za-km-z]+$/.test(trimmed);
}
function validateOptionalSignature(value: string | null, label: string): void {
if (value === null || value.trim() === "") {
return;
}
if (!isSolanaSignatureLike(value)) {
throw new Error(`${label} must be a valid Solana transaction signature.`);
}
}
function validateOnchainRequest(request: Demo3OnchainDexDiscoveryRequest): void {
if (request.signatureSource === "address") {
const sourceAddress = request.sourceAddress ?? "";
if (!isSolanaAddressLike(sourceAddress)) {
throw new Error("Signature source is 'address': Source address must be a real Solana account address, pool, vault, position, config or mint. It cannot be empty or the literal value 'address'.");
const addresses = request.sourceAddresses ?? [];
if (request.signatureSource === "address" && addresses.length === 0) {
throw new Error("Signature source is 'address': provide at least one Solana account, pool, vault, position, config or mint address.");
}
for (const address of addresses) {
if (!isSolanaAddressLike(address)) {
throw new Error(`Invalid source address '${address}'. Provide Solana account addresses separated by commas, spaces or new lines.`);
}
}
validateOptionalSignature(request.beforeSignature, "Before signature");
validateOptionalSignature(request.untilSignature, "Until signature");
if (request.programId !== null && !isSolanaAddressLike(request.programId)) {
throw new Error("Program id filter must be a valid Solana program id, or empty when using a preset that resolves it.");
}
@@ -143,6 +179,27 @@ function intValue(id: string, fallback: number): number {
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
}
function selectedTargetEvents(): string[] {
return Array.from(document.querySelectorAll<HTMLInputElement>('input[name="demo3TargetEventInput"]:checked'))
.map((input) => input.value.trim())
.filter((value) => value !== "");
}
function readTargetEventFilter(): string | null {
const selected = selectedTargetEvents();
return selected.length === 0 ? null : selected.join(",");
}
function targetEventLabel(targetEvent: string | null): string {
return targetEvent === null || targetEvent.trim() === "" ? "any" : targetEvent;
}
function clearTargetEventFilters(): void {
document.querySelectorAll<HTMLInputElement>('input[name="demo3TargetEventInput"]').forEach((input) => {
input.checked = false;
});
}
function escapeHtml(value: string): string {
return value
.replace(/&/g, "&amp;")
@@ -227,12 +284,18 @@ function applyPreset(indexText: string): void {
}
function readOnchainRequest(): Demo3OnchainDexDiscoveryRequest {
const sourceAddresses = splitSourceAddresses(byId<HTMLTextAreaElement>("demo3SourceAddressInput").value);
return {
dexCode: valueOrNull(byId<HTMLInputElement>("demo3DexCodeInput").value),
programId: valueOrNull(byId<HTMLInputElement>("demo3ProgramIdInput").value),
signatureSource: valueOrNull(byId<HTMLSelectElement>("demo3SignatureSourceSelect").value),
sourceAddress: valueOrNull(byId<HTMLInputElement>("demo3SourceAddressInput").value),
targetEvent: valueOrNull(byId<HTMLSelectElement>("demo3TargetEventSelect").value),
sourceAddress: sourceAddresses.length === 1 ? sourceAddresses[0] : null,
sourceAddresses,
beforeSignature: valueOrNull(byId<HTMLInputElement>("demo3BeforeSignatureInput").value),
untilSignature: valueOrNull(byId<HTMLInputElement>("demo3UntilSignatureInput").value),
maxPages: intValue("demo3MaxPagesInput", 1),
scanOrder: valueOrNull(byId<HTMLSelectElement>("demo3ScanOrderSelect").value),
targetEvent: readTargetEventFilter(),
excludeSwaps: byId<HTMLInputElement>("demo3ExcludeSwapsInput").checked,
includeFailed: byId<HTMLInputElement>("demo3IncludeFailedInput").checked,
httpRole: byId<HTMLInputElement>("demo3HttpRoleInput").value.trim() || "history_backfill",
@@ -258,12 +321,16 @@ function clearFilters(): void {
byId<HTMLInputElement>("demo3DexCodeInput").value = "";
byId<HTMLInputElement>("demo3ProgramIdInput").value = "";
byId<HTMLSelectElement>("demo3SignatureSourceSelect").value = "program_id";
byId<HTMLInputElement>("demo3SourceAddressInput").value = "";
byId<HTMLTextAreaElement>("demo3SourceAddressInput").value = "";
byId<HTMLInputElement>("demo3BeforeSignatureInput").value = "";
byId<HTMLInputElement>("demo3UntilSignatureInput").value = "";
byId<HTMLInputElement>("demo3MaxPagesInput").value = "1";
byId<HTMLSelectElement>("demo3ScanOrderSelect").value = "newest_first";
byId<HTMLInputElement>("demo3PairIdInput").value = "";
byId<HTMLInputElement>("demo3PoolAddressInput").value = "";
byId<HTMLInputElement>("demo3TokenMintInput").value = "";
byId<HTMLInputElement>("demo3SignatureInput").value = "";
byId<HTMLSelectElement>("demo3TargetEventSelect").value = "";
clearTargetEventFilters();
byId<HTMLInputElement>("demo3ExcludeSwapsInput").checked = false;
byId<HTMLInputElement>("demo3IncludeFailedInput").checked = true;
byId<HTMLSelectElement>("demo3PresetSelect").value = "";
@@ -272,6 +339,8 @@ function clearFilters(): void {
function renderOnchainResult(result: Demo3OnchainDexDiscoveryResult): void {
byId<HTMLElement>("demo3SummarySignatureCount").textContent = String(result.fetchedSignatureCount);
byId<HTMLElement>("demo3SummaryUniqueFetchedSignatureCount").textContent = String(result.uniqueFetchedSignatureCount);
byId<HTMLElement>("demo3SummaryFetchedPageCount").textContent = String(result.fetchedSignaturePageCount);
byId<HTMLElement>("demo3SummaryUniqueSignatureCount").textContent = String(result.uniqueSignatureCount);
byId<HTMLElement>("demo3SummaryFetchedTxCount").textContent = String(result.fetchedTransactionCount);
byId<HTMLElement>("demo3SummaryMissingTxCount").textContent = String(result.missingTransactionCount);
@@ -281,9 +350,11 @@ function renderOnchainResult(result: Demo3OnchainDexDiscoveryResult): void {
byId<HTMLElement>("demo3SummaryExtractedCandidateCount").textContent = String(result.extractedCandidateCount);
byId<HTMLElement>("demo3SummaryRejectedCandidateCount").textContent = String(result.targetRejectedCandidateCount);
byId<HTMLElement>("demo3SummaryCandidateCount").textContent = String(result.candidateCount);
const targetEvent = result.request.targetEvent ?? "any";
byId<HTMLElement>("demo3TargetText").textContent = `${result.resolvedDexCode ?? "custom"} / program=${result.resolvedProgramId} / source=${result.resolvedSignatureSource}:${result.resolvedSignatureAddress} / target=${targetEvent}`;
const targetEvent = targetEventLabel(result.request.targetEvent);
const sourceText = result.resolvedSignatureAddresses.length === 0 ? result.resolvedSignatureAddress : result.resolvedSignatureAddresses.join(",");
byId<HTMLElement>("demo3TargetText").textContent = `${result.resolvedDexCode ?? "custom"} / program=${result.resolvedProgramId} / source=${result.resolvedSignatureSource}:${sourceText} / target=${targetEvent} / order=${result.request.scanOrder ?? "newest_first"}`;
byId<HTMLElement>("demo3UniqueSignatureText").textContent = result.uniqueBackfillSignatures.length === 0 ? "-" : result.uniqueBackfillSignatures.join(", ");
byId<HTMLElement>("demo3NextBeforeText").textContent = result.nextBeforeByAddress.length === 0 ? "-" : result.nextBeforeByAddress.map((cursor) => `${cursor.address}:${cursor.nextBeforeSignature ?? "-"}`).join(" | ");
renderRejectedSummary(result);
renderOnchainCandidates(result.candidates);
}
@@ -374,14 +445,14 @@ async function discoverOnchain(): Promise<void> {
return;
}
setStatus("running", "text-bg-warning");
appendLogLine(`on-chain discovery dex='${request.dexCode ?? ""}' program='${request.programId ?? ""}' source='${request.signatureSource ?? "program_id"}:${request.sourceAddress ?? ""}' target='${request.targetEvent ?? "any"}' excludeSwaps='${request.excludeSwaps}' role='${request.httpRole}'`);
appendLogLine(`on-chain discovery dex='${request.dexCode ?? ""}' program='${request.programId ?? ""}' source='${request.signatureSource ?? "program_id"}:${request.sourceAddresses.join(",")}' target='${targetEventLabel(request.targetEvent)}' pages='${request.maxPages}' order='${request.scanOrder ?? "newest_first"}' before='${request.beforeSignature ?? ""}' until='${request.untilSignature ?? ""}' excludeSwaps='${request.excludeSwaps}' role='${request.httpRole}'`);
try {
const payload = await invoke<Demo3OnchainDexDiscoveryPayload>("demo3_discover_onchain_dex_pairs", { request });
lastResultJson = payload.resultJson;
byId<HTMLTextAreaElement>("demo3JsonTextarea").value = payload.resultJson;
renderOnchainResult(payload.result);
setStatus("ok", "text-bg-success");
appendLogLine(`on-chain discovery completed: candidates='${payload.result.candidateCount}' unique='${payload.result.uniqueSignatureCount}' signatures='${payload.result.fetchedSignatureCount}' extracted='${payload.result.extractedCandidateCount}' rejected='${payload.result.targetRejectedCandidateCount}' skippedSwapTx='${payload.result.skippedSwapLogTransactionCount}'`);
appendLogLine(`on-chain discovery completed: candidates='${payload.result.candidateCount}' unique='${payload.result.uniqueSignatureCount}' signatures='${payload.result.fetchedSignatureCount}' uniqueFetched='${payload.result.uniqueFetchedSignatureCount}' pages='${payload.result.fetchedSignaturePageCount}' extracted='${payload.result.extractedCandidateCount}' rejected='${payload.result.targetRejectedCandidateCount}' skippedSwapTx='${payload.result.skippedSwapLogTransactionCount}'`);
} catch (error) {
setStatus("error", "text-bg-danger");
appendLogLine(`on-chain discovery failed: ${String(error)}`);