This commit is contained in:
2026-06-01 19:05:46 +02:00
parent abb810d544
commit 27e25d5bf4
59 changed files with 5727 additions and 1706 deletions

View File

@@ -187,6 +187,20 @@ function validateOnchainRequest(request: Demo3OnchainDexDiscoveryRequest): void
}
throw new Error("Program id filter must be a valid Solana program id, or empty when using a preset that resolves it.");
}
validateDiscriminatorFilter(request.targetDiscriminatorHex);
}
function validateDiscriminatorFilter(value: string | null): void {
if (value === null || value.trim() === "") {
return;
}
const tokens = value.split(/[\s,]+/).map((token) => token.trim()).filter((token) => token !== "");
for (const token of tokens) {
const normalized = token.startsWith("0x") || token.startsWith("0X") ? token.slice(2) : token;
if (!/^[0-9a-fA-F]{16}$/.test(normalized)) {
throw new Error(`Target discriminator '${token}' must be exactly 8 bytes / 16 hex characters.`);
}
}
}
function numberValueOrNull(value: string): number | null {
@@ -320,6 +334,8 @@ function readOnchainRequest(): Demo3OnchainDexDiscoveryRequest {
maxPages: intValue("demo3MaxPagesInput", 1),
scanOrder: valueOrNull(byId<HTMLSelectElement>("demo3ScanOrderSelect").value),
targetEvent: readTargetEventFilter(),
targetInstructionName: valueOrNull(byId<HTMLInputElement>("demo3TargetInstructionNameInput").value),
targetDiscriminatorHex: valueOrNull(byId<HTMLInputElement>("demo3TargetDiscriminatorHexInput").value),
excludeSwaps: byId<HTMLInputElement>("demo3ExcludeSwapsInput").checked,
includeFailed: byId<HTMLInputElement>("demo3IncludeFailedInput").checked,
httpRole: byId<HTMLInputElement>("demo3HttpRoleInput").value.trim() || "history_backfill",
@@ -375,8 +391,10 @@ function renderOnchainResult(result: Demo3OnchainDexDiscoveryResult): void {
byId<HTMLElement>("demo3SummaryRejectedCandidateCount").textContent = String(result.targetRejectedCandidateCount);
byId<HTMLElement>("demo3SummaryCandidateCount").textContent = String(result.candidateCount);
const targetEvent = targetEventLabel(result.request.targetEvent);
const targetInstruction = result.request.targetInstructionName ?? "any";
const targetDiscriminator = result.request.targetDiscriminatorHex ?? "any";
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>("demo3TargetText").textContent = `${result.resolvedDexCode ?? "custom"} / program=${result.resolvedProgramId} / source=${result.resolvedSignatureSource}:${sourceText} / target=${targetEvent} / instr=${targetInstruction} / disc=${targetDiscriminator} / 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);
@@ -402,7 +420,7 @@ function renderRejectedSummary(result: Demo3OnchainDexDiscoveryResult): void {
function renderOnchainCandidates(candidates: Demo3OnchainDexPairCandidate[]): void {
const body = byId<HTMLTableSectionElement>("demo3OnchainCandidateTableBody");
if (candidates.length === 0) {
body.innerHTML = '<tr><td colspan="12" class="text-body-secondary">No on-chain candidate.</td></tr>';
body.innerHTML = '<tr><td colspan="13" class="text-body-secondary">No on-chain candidate.</td></tr>';
return;
}
body.innerHTML = candidates.map((candidate) => {
@@ -428,6 +446,7 @@ function renderOnchainCandidates(candidates: Demo3OnchainDexPairCandidate[]): vo
<td><span class="badge text-bg-info">${escapeHtml(candidate.candidateKind)}</span></td>
<td><span class="badge text-bg-${candidate.confidence === "high" ? "success" : candidate.confidence === "medium" ? "warning" : "secondary"}">${escapeHtml(candidate.confidence)}</span></td>
<td class="font-monospace" title="${escapeHtml(candidate.instructionDataPrefix ?? "")}">${escapeHtml(shortText(candidate.instructionDataPrefix, 14))}</td>
<td class="font-monospace" title="${escapeHtml(candidate.instructionDiscriminatorHex ?? "")}">${escapeHtml(shortText(candidate.instructionDiscriminatorHex, 16))}</td>
<td class="font-monospace" title="${escapeHtml(verifiedPool ?? "")}">${escapeHtml(shortText(verifiedPool, 14))}</td>
<td class="font-monospace" title="${escapeHtml(candidate.tokenAMint ?? "")}">${escapeHtml(shortText(candidate.tokenAMint, 14))}</td>
<td class="font-monospace" title="${escapeHtml(candidate.tokenBMint ?? "")}">${escapeHtml(shortText(candidate.tokenBMint, 14))}</td>
@@ -469,7 +488,7 @@ 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.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}'`);
appendLogLine(`on-chain discovery dex='${request.dexCode ?? ""}' program='${request.programId ?? ""}' source='${request.signatureSource ?? "program_id"}:${request.sourceAddresses.join(",")}' target='${targetEventLabel(request.targetEvent)}' instruction='${request.targetInstructionName ?? ""}' discriminator='${request.targetDiscriminatorHex ?? ""}' 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;