0.7.40
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Matching decoded event sample for corpus discovery.
|
||||
*/
|
||||
export type Demo3LocalDexCorpusDecodedEventSample = {
|
||||
/**
|
||||
* Decoded event id.
|
||||
*/
|
||||
decodedEventId: number,
|
||||
/**
|
||||
* Transaction id.
|
||||
*/
|
||||
transactionId: number,
|
||||
/**
|
||||
* Transaction signature.
|
||||
*/
|
||||
signature: string,
|
||||
/**
|
||||
* Optional Solana slot.
|
||||
*/
|
||||
slot: number | null,
|
||||
/**
|
||||
* Protocol name stored on the decoded event.
|
||||
*/
|
||||
protocolName: string,
|
||||
/**
|
||||
* Program id stored on the decoded event.
|
||||
*/
|
||||
programId: string,
|
||||
/**
|
||||
* Event kind.
|
||||
*/
|
||||
eventKind: string,
|
||||
/**
|
||||
* Optional pool account.
|
||||
*/
|
||||
poolAccount: string | null,
|
||||
/**
|
||||
* Optional token A mint.
|
||||
*/
|
||||
tokenAMint: string | null,
|
||||
/**
|
||||
* Optional token B mint.
|
||||
*/
|
||||
tokenBMint: string | null,
|
||||
/**
|
||||
* Decoded event category extracted from payload JSON.
|
||||
*/
|
||||
eventCategory: string | null,
|
||||
/**
|
||||
* Decoded event lifecycle kind extracted from payload JSON.
|
||||
*/
|
||||
eventLifecycleKind: string | null,
|
||||
/**
|
||||
* Decoded event actionability extracted from payload JSON.
|
||||
*/
|
||||
eventActionability: string | null,
|
||||
/**
|
||||
* Whether the decoded event is a trade candidate.
|
||||
*/
|
||||
tradeCandidate: boolean,
|
||||
/**
|
||||
* Whether the decoded event is a candle candidate.
|
||||
*/
|
||||
candleCandidate: boolean, };
|
||||
@@ -0,0 +1,58 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Matching pool/pair sample for corpus discovery.
|
||||
*/
|
||||
export type Demo3LocalDexCorpusPoolPairSample = {
|
||||
/**
|
||||
* Optional pool id.
|
||||
*/
|
||||
poolId: number | null,
|
||||
/**
|
||||
* Optional pool address.
|
||||
*/
|
||||
poolAddress: string | null,
|
||||
/**
|
||||
* Optional pair id.
|
||||
*/
|
||||
pairId: number | null,
|
||||
/**
|
||||
* Optional DEX code.
|
||||
*/
|
||||
dexCode: string | null,
|
||||
/**
|
||||
* Optional pair symbol.
|
||||
*/
|
||||
pairSymbol: string | null,
|
||||
/**
|
||||
* Optional base token mint.
|
||||
*/
|
||||
baseMint: string | null,
|
||||
/**
|
||||
* Optional base token symbol.
|
||||
*/
|
||||
baseSymbol: string | null,
|
||||
/**
|
||||
* Optional quote token mint.
|
||||
*/
|
||||
quoteMint: string | null,
|
||||
/**
|
||||
* Optional quote token symbol.
|
||||
*/
|
||||
quoteSymbol: string | null,
|
||||
/**
|
||||
* Number of decoded events attached to the pool.
|
||||
*/
|
||||
decodedEventCount: number,
|
||||
/**
|
||||
* Number of trade events attached to the pair.
|
||||
*/
|
||||
tradeEventCount: number,
|
||||
/**
|
||||
* Number of candle rows attached to the pair.
|
||||
*/
|
||||
pairCandleCount: number,
|
||||
/**
|
||||
* Latest known founding or activity signature for the pool/pair.
|
||||
*/
|
||||
latestSignature: string | null, };
|
||||
@@ -0,0 +1,19 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { Demo3LocalDexCorpusSearchResult } from "./Demo3LocalDexCorpusSearchResult";
|
||||
|
||||
/**
|
||||
* Response payload returned by Demo3 local DEX corpus search.
|
||||
*/
|
||||
export type Demo3LocalDexCorpusSearchPayload = {
|
||||
/**
|
||||
* Open database URL.
|
||||
*/
|
||||
databaseUrl: string,
|
||||
/**
|
||||
* Pretty JSON representation of the search result.
|
||||
*/
|
||||
resultJson: string,
|
||||
/**
|
||||
* Structured local DEX corpus search result.
|
||||
*/
|
||||
result: Demo3LocalDexCorpusSearchResult, };
|
||||
@@ -0,0 +1,34 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Request payload for a local DEX corpus search.
|
||||
*/
|
||||
export type Demo3LocalDexCorpusSearchRequest = {
|
||||
/**
|
||||
* Optional DEX code or decoded protocol name.
|
||||
*/
|
||||
dexCode: string | null,
|
||||
/**
|
||||
* Optional Solana program id.
|
||||
*/
|
||||
programId: string | null,
|
||||
/**
|
||||
* Optional local pair id.
|
||||
*/
|
||||
pairId: number | null,
|
||||
/**
|
||||
* Optional pool account/address.
|
||||
*/
|
||||
poolAddress: string | null,
|
||||
/**
|
||||
* Optional token mint to match as base, quote or decoded mint.
|
||||
*/
|
||||
tokenMint: string | null,
|
||||
/**
|
||||
* Optional transaction signature.
|
||||
*/
|
||||
signature: string | null,
|
||||
/**
|
||||
* Maximum number of rows to return per sample category.
|
||||
*/
|
||||
limit: number, };
|
||||
@@ -0,0 +1,31 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { Demo3LocalDexCorpusDecodedEventSample } from "./Demo3LocalDexCorpusDecodedEventSample";
|
||||
import type { Demo3LocalDexCorpusPoolPairSample } from "./Demo3LocalDexCorpusPoolPairSample";
|
||||
import type { Demo3LocalDexCorpusSearchRequest } from "./Demo3LocalDexCorpusSearchRequest";
|
||||
import type { Demo3LocalDexCorpusSearchSummary } from "./Demo3LocalDexCorpusSearchSummary";
|
||||
import type { Demo3LocalDexCorpusTransactionSample } from "./Demo3LocalDexCorpusTransactionSample";
|
||||
|
||||
/**
|
||||
* Structured local DEX corpus search result.
|
||||
*/
|
||||
export type Demo3LocalDexCorpusSearchResult = {
|
||||
/**
|
||||
* Normalized search request applied by the backend service.
|
||||
*/
|
||||
request: Demo3LocalDexCorpusSearchRequest,
|
||||
/**
|
||||
* Aggregate counts for the matching local data.
|
||||
*/
|
||||
summary: Demo3LocalDexCorpusSearchSummary,
|
||||
/**
|
||||
* Matching transaction samples.
|
||||
*/
|
||||
transactionSamples: Array<Demo3LocalDexCorpusTransactionSample>,
|
||||
/**
|
||||
* Matching pool/pair samples.
|
||||
*/
|
||||
poolPairSamples: Array<Demo3LocalDexCorpusPoolPairSample>,
|
||||
/**
|
||||
* Matching decoded event samples.
|
||||
*/
|
||||
decodedEventSamples: Array<Demo3LocalDexCorpusDecodedEventSample>, };
|
||||
@@ -0,0 +1,38 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Aggregate counts for one local DEX corpus search.
|
||||
*/
|
||||
export type Demo3LocalDexCorpusSearchSummary = {
|
||||
/**
|
||||
* Number of distinct matching transactions.
|
||||
*/
|
||||
transactionCount: number,
|
||||
/**
|
||||
* Number of distinct matching instructions.
|
||||
*/
|
||||
instructionCount: number,
|
||||
/**
|
||||
* Number of distinct matching decoded DEX events.
|
||||
*/
|
||||
decodedEventCount: number,
|
||||
/**
|
||||
* Number of distinct matching pools.
|
||||
*/
|
||||
poolCount: number,
|
||||
/**
|
||||
* Number of distinct matching pairs.
|
||||
*/
|
||||
pairCount: number,
|
||||
/**
|
||||
* Number of distinct matching trade events.
|
||||
*/
|
||||
tradeEventCount: number,
|
||||
/**
|
||||
* Number of distinct matching candle rows.
|
||||
*/
|
||||
pairCandleCount: number,
|
||||
/**
|
||||
* Number of distinct matching protocol candidate rows.
|
||||
*/
|
||||
protocolCandidateCount: number, };
|
||||
@@ -0,0 +1,38 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Matching transaction sample for corpus discovery.
|
||||
*/
|
||||
export type Demo3LocalDexCorpusTransactionSample = {
|
||||
/**
|
||||
* Transaction id.
|
||||
*/
|
||||
transactionId: number,
|
||||
/**
|
||||
* Transaction signature.
|
||||
*/
|
||||
signature: string,
|
||||
/**
|
||||
* Optional Solana slot.
|
||||
*/
|
||||
slot: number | null,
|
||||
/**
|
||||
* Whether the transaction has a non-null error payload.
|
||||
*/
|
||||
failed: boolean,
|
||||
/**
|
||||
* Number of persisted instructions for the transaction.
|
||||
*/
|
||||
instructionCount: number,
|
||||
/**
|
||||
* Number of persisted decoded DEX events for the transaction.
|
||||
*/
|
||||
decodedEventCount: number,
|
||||
/**
|
||||
* Number of persisted trade events for the transaction.
|
||||
*/
|
||||
tradeEventCount: number,
|
||||
/**
|
||||
* Comma-separated distinct program ids seen in the transaction.
|
||||
*/
|
||||
programIdsCsv: string, };
|
||||
@@ -0,0 +1,34 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Candidate account inferred from generic on-chain transaction evidence.
|
||||
*/
|
||||
export type Demo3OnchainDexCandidateAccount = {
|
||||
/**
|
||||
* Account address.
|
||||
*/
|
||||
address: string,
|
||||
/**
|
||||
* Account index in the transaction message when known.
|
||||
*/
|
||||
accountIndex: number | null,
|
||||
/**
|
||||
* Whether the account is writable in the transaction message when known.
|
||||
*/
|
||||
writable: boolean | null,
|
||||
/**
|
||||
* Whether the account is a signer in the transaction message when known.
|
||||
*/
|
||||
signer: boolean | null,
|
||||
/**
|
||||
* Generic role inferred by Demo3.
|
||||
*/
|
||||
inferredRole: string,
|
||||
/**
|
||||
* Confidence of the generic account inference.
|
||||
*/
|
||||
confidence: string,
|
||||
/**
|
||||
* Short reason explaining why the account is listed.
|
||||
*/
|
||||
reason: string, };
|
||||
@@ -0,0 +1,19 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { Demo3OnchainDexDiscoveryResult } from "./Demo3OnchainDexDiscoveryResult";
|
||||
|
||||
/**
|
||||
* Response payload returned by Demo3 on-chain DEX discovery.
|
||||
*/
|
||||
export type Demo3OnchainDexDiscoveryPayload = {
|
||||
/**
|
||||
* HTTP role used by the request.
|
||||
*/
|
||||
httpRole: string,
|
||||
/**
|
||||
* Pretty JSON representation of the discovery result.
|
||||
*/
|
||||
resultJson: string,
|
||||
/**
|
||||
* Structured discovery result.
|
||||
*/
|
||||
result: Demo3OnchainDexDiscoveryResult, };
|
||||
@@ -0,0 +1,30 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Request payload for on-chain DEX pair/pool discovery.
|
||||
*/
|
||||
export type Demo3OnchainDexDiscoveryRequest = {
|
||||
/**
|
||||
* Optional DEX code from the support matrix.
|
||||
*/
|
||||
dexCode: string | null,
|
||||
/**
|
||||
* Optional Solana program id. When absent, dex_code must resolve to a verified program id.
|
||||
*/
|
||||
programId: string | null,
|
||||
/**
|
||||
* HTTP role used to query Solana RPC.
|
||||
*/
|
||||
httpRole: string,
|
||||
/**
|
||||
* Maximum number of signatures to inspect.
|
||||
*/
|
||||
signatureLimit: number,
|
||||
/**
|
||||
* Maximum number of transactions to fetch from the signature list.
|
||||
*/
|
||||
transactionLimit: number,
|
||||
/**
|
||||
* Maximum number of candidate rows to return.
|
||||
*/
|
||||
candidateLimit: number, };
|
||||
@@ -0,0 +1,44 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { Demo3OnchainDexDiscoveryRequest } from "./Demo3OnchainDexDiscoveryRequest";
|
||||
import type { Demo3OnchainDexPairCandidate } from "./Demo3OnchainDexPairCandidate";
|
||||
|
||||
/**
|
||||
* Structured on-chain DEX discovery result.
|
||||
*/
|
||||
export type Demo3OnchainDexDiscoveryResult = {
|
||||
/**
|
||||
* Normalized request used by kb_lib.
|
||||
*/
|
||||
request: Demo3OnchainDexDiscoveryRequest,
|
||||
/**
|
||||
* DEX code resolved from the support matrix when available.
|
||||
*/
|
||||
resolvedDexCode: string | null,
|
||||
/**
|
||||
* Program id scanned with getSignaturesForAddress.
|
||||
*/
|
||||
resolvedProgramId: string,
|
||||
/**
|
||||
* Number of signatures returned by Solana RPC.
|
||||
*/
|
||||
fetchedSignatureCount: number,
|
||||
/**
|
||||
* Number of fetched transactions.
|
||||
*/
|
||||
fetchedTransactionCount: number,
|
||||
/**
|
||||
* Number of getTransaction calls returning null.
|
||||
*/
|
||||
missingTransactionCount: number,
|
||||
/**
|
||||
* Number of failed transactions encountered.
|
||||
*/
|
||||
failedTransactionCount: number,
|
||||
/**
|
||||
* Number of candidate rows returned.
|
||||
*/
|
||||
candidateCount: number,
|
||||
/**
|
||||
* Candidate on-chain rows.
|
||||
*/
|
||||
candidates: Array<Demo3OnchainDexPairCandidate>, };
|
||||
100
kb_demo_app/frontend/ts/bindings/Demo3OnchainDexPairCandidate.ts
Normal file
100
kb_demo_app/frontend/ts/bindings/Demo3OnchainDexPairCandidate.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { Demo3OnchainDexCandidateAccount } from "./Demo3OnchainDexCandidateAccount";
|
||||
import type { Demo3OnchainDexTokenBalanceDelta } from "./Demo3OnchainDexTokenBalanceDelta";
|
||||
|
||||
/**
|
||||
* Candidate on-chain transaction/instruction for a DEX program id.
|
||||
*/
|
||||
export type Demo3OnchainDexPairCandidate = {
|
||||
/**
|
||||
* Transaction signature.
|
||||
*/
|
||||
signature: string,
|
||||
/**
|
||||
* Slot when available.
|
||||
*/
|
||||
slot: number | null,
|
||||
/**
|
||||
* Block time when available.
|
||||
*/
|
||||
blockTime: number | null,
|
||||
/**
|
||||
* Whether the transaction failed.
|
||||
*/
|
||||
failed: boolean,
|
||||
/**
|
||||
* Program id matched by the candidate.
|
||||
*/
|
||||
programId: string,
|
||||
/**
|
||||
* DEX code when known.
|
||||
*/
|
||||
dexCode: string | null,
|
||||
/**
|
||||
* Candidate kind inferred from data/logs.
|
||||
*/
|
||||
candidateKind: string,
|
||||
/**
|
||||
* Extraction confidence.
|
||||
*/
|
||||
confidence: string,
|
||||
/**
|
||||
* Top-level instruction index.
|
||||
*/
|
||||
instructionIndex: number | null,
|
||||
/**
|
||||
* Inner instruction index.
|
||||
*/
|
||||
innerInstructionIndex: number | null,
|
||||
/**
|
||||
* Instruction name inferred from data/logs.
|
||||
*/
|
||||
instructionName: string | null,
|
||||
/**
|
||||
* Candidate pool address.
|
||||
*/
|
||||
poolAddress: string | null,
|
||||
/**
|
||||
* Candidate token A mint.
|
||||
*/
|
||||
tokenAMint: string | null,
|
||||
/**
|
||||
* Candidate token B mint.
|
||||
*/
|
||||
tokenBMint: string | null,
|
||||
/**
|
||||
* Verified pool address when a DEX decoder or stable layout proves it.
|
||||
*/
|
||||
verifiedPoolAddress: string | null,
|
||||
/**
|
||||
* Token mints observed generically from transaction token balances.
|
||||
*/
|
||||
observedTokenMints: Array<string>,
|
||||
/**
|
||||
* Token balance deltas observed through transaction metadata.
|
||||
*/
|
||||
tokenBalanceDeltas: Array<Demo3OnchainDexTokenBalanceDelta>,
|
||||
/**
|
||||
* Program-owned or writable accounts that may be pool/config/state accounts.
|
||||
*/
|
||||
candidatePoolAccounts: Array<Demo3OnchainDexCandidateAccount>,
|
||||
/**
|
||||
* Token accounts that may be pool vaults.
|
||||
*/
|
||||
candidateTokenVaultAccounts: Array<Demo3OnchainDexCandidateAccount>,
|
||||
/**
|
||||
* Other candidate accounts attached to the matched instruction.
|
||||
*/
|
||||
candidateProgramAccounts: Array<Demo3OnchainDexCandidateAccount>,
|
||||
/**
|
||||
* Short account sample.
|
||||
*/
|
||||
accountSamples: Array<string>,
|
||||
/**
|
||||
* Short log sample.
|
||||
*/
|
||||
logSamples: Array<string>,
|
||||
/**
|
||||
* Suggested next action.
|
||||
*/
|
||||
backfillHint: string, };
|
||||
@@ -0,0 +1,38 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Token-balance delta observed in one on-chain candidate transaction.
|
||||
*/
|
||||
export type Demo3OnchainDexTokenBalanceDelta = {
|
||||
/**
|
||||
* Token account index in the transaction message when available.
|
||||
*/
|
||||
accountIndex: number | null,
|
||||
/**
|
||||
* Token account address resolved from the transaction account keys.
|
||||
*/
|
||||
accountAddress: string | null,
|
||||
/**
|
||||
* SPL Token or Token-2022 mint address.
|
||||
*/
|
||||
mint: string,
|
||||
/**
|
||||
* Token account owner when Solana RPC exposes it.
|
||||
*/
|
||||
owner: string | null,
|
||||
/**
|
||||
* Token program id when Solana RPC exposes it.
|
||||
*/
|
||||
tokenProgram: string | null,
|
||||
/**
|
||||
* Raw token amount before the transaction.
|
||||
*/
|
||||
preAmountRaw: string | null,
|
||||
/**
|
||||
* Raw token amount after the transaction.
|
||||
*/
|
||||
postAmountRaw: string | null,
|
||||
/**
|
||||
* Signed raw delta when calculable.
|
||||
*/
|
||||
deltaRaw: string | null, };
|
||||
@@ -0,0 +1,66 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Matching decoded event sample for corpus discovery.
|
||||
*/
|
||||
export type Demo3oldLocalDexCorpusDecodedEventSample = {
|
||||
/**
|
||||
* Decoded event id.
|
||||
*/
|
||||
decodedEventId: number,
|
||||
/**
|
||||
* Transaction id.
|
||||
*/
|
||||
transactionId: number,
|
||||
/**
|
||||
* Transaction signature.
|
||||
*/
|
||||
signature: string,
|
||||
/**
|
||||
* Optional Solana slot.
|
||||
*/
|
||||
slot: number | null,
|
||||
/**
|
||||
* Protocol name stored on the decoded event.
|
||||
*/
|
||||
protocolName: string,
|
||||
/**
|
||||
* Program id stored on the decoded event.
|
||||
*/
|
||||
programId: string,
|
||||
/**
|
||||
* Event kind.
|
||||
*/
|
||||
eventKind: string,
|
||||
/**
|
||||
* Optional pool account.
|
||||
*/
|
||||
poolAccount: string | null,
|
||||
/**
|
||||
* Optional token A mint.
|
||||
*/
|
||||
tokenAMint: string | null,
|
||||
/**
|
||||
* Optional token B mint.
|
||||
*/
|
||||
tokenBMint: string | null,
|
||||
/**
|
||||
* Decoded event category extracted from payload JSON.
|
||||
*/
|
||||
eventCategory: string | null,
|
||||
/**
|
||||
* Decoded event lifecycle kind extracted from payload JSON.
|
||||
*/
|
||||
eventLifecycleKind: string | null,
|
||||
/**
|
||||
* Decoded event actionability extracted from payload JSON.
|
||||
*/
|
||||
eventActionability: string | null,
|
||||
/**
|
||||
* Whether the decoded event is a trade candidate.
|
||||
*/
|
||||
tradeCandidate: boolean,
|
||||
/**
|
||||
* Whether the decoded event is a candle candidate.
|
||||
*/
|
||||
candleCandidate: boolean, };
|
||||
@@ -0,0 +1,58 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Matching pool/pair sample for corpus discovery.
|
||||
*/
|
||||
export type Demo3oldLocalDexCorpusPoolPairSample = {
|
||||
/**
|
||||
* Optional pool id.
|
||||
*/
|
||||
poolId: number | null,
|
||||
/**
|
||||
* Optional pool address.
|
||||
*/
|
||||
poolAddress: string | null,
|
||||
/**
|
||||
* Optional pair id.
|
||||
*/
|
||||
pairId: number | null,
|
||||
/**
|
||||
* Optional DEX code.
|
||||
*/
|
||||
dexCode: string | null,
|
||||
/**
|
||||
* Optional pair symbol.
|
||||
*/
|
||||
pairSymbol: string | null,
|
||||
/**
|
||||
* Optional base token mint.
|
||||
*/
|
||||
baseMint: string | null,
|
||||
/**
|
||||
* Optional base token symbol.
|
||||
*/
|
||||
baseSymbol: string | null,
|
||||
/**
|
||||
* Optional quote token mint.
|
||||
*/
|
||||
quoteMint: string | null,
|
||||
/**
|
||||
* Optional quote token symbol.
|
||||
*/
|
||||
quoteSymbol: string | null,
|
||||
/**
|
||||
* Number of decoded events attached to the pool.
|
||||
*/
|
||||
decodedEventCount: number,
|
||||
/**
|
||||
* Number of trade events attached to the pair.
|
||||
*/
|
||||
tradeEventCount: number,
|
||||
/**
|
||||
* Number of candle rows attached to the pair.
|
||||
*/
|
||||
pairCandleCount: number,
|
||||
/**
|
||||
* Latest known founding or activity signature for the pool/pair.
|
||||
*/
|
||||
latestSignature: string | null, };
|
||||
@@ -0,0 +1,19 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { Demo3oldLocalDexCorpusSearchResult } from "./Demo3oldLocalDexCorpusSearchResult";
|
||||
|
||||
/**
|
||||
* Response payload returned by Demo3old local DEX corpus search.
|
||||
*/
|
||||
export type Demo3oldLocalDexCorpusSearchPayload = {
|
||||
/**
|
||||
* Open database URL.
|
||||
*/
|
||||
databaseUrl: string,
|
||||
/**
|
||||
* Pretty JSON representation of the search result.
|
||||
*/
|
||||
resultJson: string,
|
||||
/**
|
||||
* Structured local DEX corpus search result.
|
||||
*/
|
||||
result: Demo3oldLocalDexCorpusSearchResult, };
|
||||
@@ -0,0 +1,34 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Request payload for a local DEX corpus search.
|
||||
*/
|
||||
export type Demo3oldLocalDexCorpusSearchRequest = {
|
||||
/**
|
||||
* Optional DEX code or decoded protocol name.
|
||||
*/
|
||||
dexCode: string | null,
|
||||
/**
|
||||
* Optional Solana program id.
|
||||
*/
|
||||
programId: string | null,
|
||||
/**
|
||||
* Optional local pair id.
|
||||
*/
|
||||
pairId: number | null,
|
||||
/**
|
||||
* Optional pool account/address.
|
||||
*/
|
||||
poolAddress: string | null,
|
||||
/**
|
||||
* Optional token mint to match as base, quote or decoded mint.
|
||||
*/
|
||||
tokenMint: string | null,
|
||||
/**
|
||||
* Optional transaction signature.
|
||||
*/
|
||||
signature: string | null,
|
||||
/**
|
||||
* Maximum number of rows to return per sample category.
|
||||
*/
|
||||
limit: number, };
|
||||
@@ -0,0 +1,31 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { Demo3oldLocalDexCorpusDecodedEventSample } from "./Demo3oldLocalDexCorpusDecodedEventSample";
|
||||
import type { Demo3oldLocalDexCorpusPoolPairSample } from "./Demo3oldLocalDexCorpusPoolPairSample";
|
||||
import type { Demo3oldLocalDexCorpusSearchRequest } from "./Demo3oldLocalDexCorpusSearchRequest";
|
||||
import type { Demo3oldLocalDexCorpusSearchSummary } from "./Demo3oldLocalDexCorpusSearchSummary";
|
||||
import type { Demo3oldLocalDexCorpusTransactionSample } from "./Demo3oldLocalDexCorpusTransactionSample";
|
||||
|
||||
/**
|
||||
* Structured local DEX corpus search result.
|
||||
*/
|
||||
export type Demo3oldLocalDexCorpusSearchResult = {
|
||||
/**
|
||||
* Normalized search request applied by the backend service.
|
||||
*/
|
||||
request: Demo3oldLocalDexCorpusSearchRequest,
|
||||
/**
|
||||
* Aggregate counts for the matching local data.
|
||||
*/
|
||||
summary: Demo3oldLocalDexCorpusSearchSummary,
|
||||
/**
|
||||
* Matching transaction samples.
|
||||
*/
|
||||
transactionSamples: Array<Demo3oldLocalDexCorpusTransactionSample>,
|
||||
/**
|
||||
* Matching pool/pair samples.
|
||||
*/
|
||||
poolPairSamples: Array<Demo3oldLocalDexCorpusPoolPairSample>,
|
||||
/**
|
||||
* Matching decoded event samples.
|
||||
*/
|
||||
decodedEventSamples: Array<Demo3oldLocalDexCorpusDecodedEventSample>, };
|
||||
@@ -0,0 +1,38 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Aggregate counts for one local DEX corpus search.
|
||||
*/
|
||||
export type Demo3oldLocalDexCorpusSearchSummary = {
|
||||
/**
|
||||
* Number of distinct matching transactions.
|
||||
*/
|
||||
transactionCount: number,
|
||||
/**
|
||||
* Number of distinct matching instructions.
|
||||
*/
|
||||
instructionCount: number,
|
||||
/**
|
||||
* Number of distinct matching decoded DEX events.
|
||||
*/
|
||||
decodedEventCount: number,
|
||||
/**
|
||||
* Number of distinct matching pools.
|
||||
*/
|
||||
poolCount: number,
|
||||
/**
|
||||
* Number of distinct matching pairs.
|
||||
*/
|
||||
pairCount: number,
|
||||
/**
|
||||
* Number of distinct matching trade events.
|
||||
*/
|
||||
tradeEventCount: number,
|
||||
/**
|
||||
* Number of distinct matching candle rows.
|
||||
*/
|
||||
pairCandleCount: number,
|
||||
/**
|
||||
* Number of distinct matching protocol candidate rows.
|
||||
*/
|
||||
protocolCandidateCount: number, };
|
||||
@@ -0,0 +1,38 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Matching transaction sample for corpus discovery.
|
||||
*/
|
||||
export type Demo3oldLocalDexCorpusTransactionSample = {
|
||||
/**
|
||||
* Transaction id.
|
||||
*/
|
||||
transactionId: number,
|
||||
/**
|
||||
* Transaction signature.
|
||||
*/
|
||||
signature: string,
|
||||
/**
|
||||
* Optional Solana slot.
|
||||
*/
|
||||
slot: number | null,
|
||||
/**
|
||||
* Whether the transaction has a non-null error payload.
|
||||
*/
|
||||
failed: boolean,
|
||||
/**
|
||||
* Number of persisted instructions for the transaction.
|
||||
*/
|
||||
instructionCount: number,
|
||||
/**
|
||||
* Number of persisted decoded DEX events for the transaction.
|
||||
*/
|
||||
decodedEventCount: number,
|
||||
/**
|
||||
* Number of persisted trade events for the transaction.
|
||||
*/
|
||||
tradeEventCount: number,
|
||||
/**
|
||||
* Comma-separated distinct program ids seen in the transaction.
|
||||
*/
|
||||
programIdsCsv: string, };
|
||||
@@ -10,7 +10,7 @@ export type DemoPipeline2BackfillPayload = {
|
||||
*/
|
||||
objectKey: string,
|
||||
/**
|
||||
* Mode: `tokenMint` or `poolAddress`.
|
||||
* Mode: `tokenMint`, `poolAddress` or `signature`.
|
||||
*/
|
||||
mode: string,
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Request payload for single-signature backfill.
|
||||
*/
|
||||
export type DemoPipeline2BackfillSignatureRequest = {
|
||||
/**
|
||||
* Transaction signature to resolve and replay.
|
||||
*/
|
||||
signature: string,
|
||||
/**
|
||||
* Optional HTTP role.
|
||||
*/
|
||||
httpRole: string | null, };
|
||||
365
kb_demo_app/frontend/ts/demo3.ts
Normal file
365
kb_demo_app/frontend/ts/demo3.ts
Normal file
@@ -0,0 +1,365 @@
|
||||
// file: kb_demo_app/frontend/ts/demo3.ts
|
||||
|
||||
import * as bootstrap from "bootstrap";
|
||||
import "simplebar";
|
||||
import ResizeObserver from "resize-observer-polyfill";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { debug, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
|
||||
import type { Demo3LocalDexCorpusSearchRequest } from "./bindings/Demo3LocalDexCorpusSearchRequest.ts";
|
||||
import type { Demo3LocalDexCorpusSearchPayload } from "./bindings/Demo3LocalDexCorpusSearchPayload.ts";
|
||||
import type { Demo3OnchainDexDiscoveryRequest } from "./bindings/Demo3OnchainDexDiscoveryRequest.ts";
|
||||
import type { Demo3OnchainDexDiscoveryResult } from "./bindings/Demo3OnchainDexDiscoveryResult.ts";
|
||||
import type { Demo3OnchainDexPairCandidate } from "./bindings/Demo3OnchainDexPairCandidate.ts";
|
||||
import type { Demo3LocalDexCorpusSearchResult } from "./bindings/Demo3LocalDexCorpusSearchResult.ts";
|
||||
import type { Demo3OnchainDexDiscoveryPayload } from "./bindings/Demo3OnchainDexDiscoveryPayload.ts";
|
||||
|
||||
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
||||
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
||||
|
||||
|
||||
interface Demo3Preset {
|
||||
label: string;
|
||||
dexCode: string;
|
||||
programId: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const presets: Demo3Preset[] = [
|
||||
{ label: "PumpSwap", dexCode: "pump_swap", programId: "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA", description: "DEX effectif PumpSwap." },
|
||||
{ label: "Raydium CPMM", dexCode: "raydium_cpmm", programId: "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C", description: "Raydium CPMM." },
|
||||
{ label: "Raydium CLMM", dexCode: "raydium_clmm", programId: "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK", description: "Raydium CLMM." },
|
||||
{ label: "Raydium AMM v4", dexCode: "raydium_amm_v4", programId: "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8", description: "Raydium AMM v4 legacy. À prouver par corpus avant décodage swap." },
|
||||
{ label: "Raydium Stable Swap", dexCode: "raydium_stable_swap", programId: "5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h", description: "Stable Swap Raydium à vérifier par corpus." },
|
||||
{ label: "Meteora DLMM", dexCode: "meteora_dlmm", programId: "LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo", description: "Meteora DLMM." },
|
||||
{ label: "Meteora DAMM v1", dexCode: "meteora_damm_v1", programId: "Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB", description: "Meteora DAMM v1." },
|
||||
{ label: "Meteora DAMM v2", dexCode: "meteora_damm_v2", programId: "cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG", description: "Meteora DAMM v2." },
|
||||
{ label: "Meteora DBC", dexCode: "meteora_dbc", programId: "dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN", description: "Meteora DBC." },
|
||||
{ label: "Orca Whirlpools", dexCode: "orca_whirlpools", programId: "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc", description: "Orca Whirlpools CLMM." },
|
||||
{ label: "FluxBeam", dexCode: "fluxbeam", programId: "FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X", description: "FluxBeam." },
|
||||
{ label: "DexLab", dexCode: "dexlab", programId: "DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N", description: "DexLab Swap/Pool." },
|
||||
{ label: "metaDAO", dexCode: "metadao", programId: "", description: "DEX à vérifier. Aucun program id n'est inventé." },
|
||||
{ label: "Printr", dexCode: "printr", programId: "", description: "DEX à vérifier. Aucun program id n'est inventé." },
|
||||
];
|
||||
|
||||
let lastResultJson = "";
|
||||
|
||||
function byId<T extends HTMLElement>(id: string): T {
|
||||
const element = document.getElementById(id);
|
||||
if (element === null) {
|
||||
throw new Error(`missing element #${id}`);
|
||||
}
|
||||
return element as T;
|
||||
}
|
||||
|
||||
function valueOrNull(value: string): string | null {
|
||||
const trimmed = value.trim();
|
||||
return trimmed === "" ? null : trimmed;
|
||||
}
|
||||
|
||||
function numberValueOrNull(value: string): number | null {
|
||||
const trimmed = value.trim();
|
||||
if (trimmed === "") {
|
||||
return null;
|
||||
}
|
||||
const parsed = Number.parseInt(trimmed, 10);
|
||||
return Number.isFinite(parsed) ? parsed : null;
|
||||
}
|
||||
|
||||
function intValue(id: string, fallback: number): number {
|
||||
const parsed = Number.parseInt(byId<HTMLInputElement>(id).value, 10);
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
||||
}
|
||||
|
||||
function escapeHtml(value: string): string {
|
||||
return value
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
function shortText(value: string | null, maxLength: number): string {
|
||||
if (value === null) {
|
||||
return "-";
|
||||
}
|
||||
if (value.length <= maxLength) {
|
||||
return value;
|
||||
}
|
||||
return `${value.slice(0, maxLength)}…`;
|
||||
}
|
||||
|
||||
function shortList(values: string[], maxItems: number, itemLength: number): string {
|
||||
if (values.length === 0) {
|
||||
return "-";
|
||||
}
|
||||
return values.slice(0, maxItems).map((value) => shortText(value, itemLength)).join(", ");
|
||||
}
|
||||
|
||||
function candidateAccountList(accounts: Demo3OnchainDexPairCandidate["candidatePoolAccounts"], maxItems: number): string {
|
||||
if (accounts.length === 0) {
|
||||
return "-";
|
||||
}
|
||||
return accounts.slice(0, maxItems).map((account) => shortText(account.address, 14)).join(", ");
|
||||
}
|
||||
|
||||
function tokenDeltaList(candidate: Demo3OnchainDexPairCandidate): string {
|
||||
if (candidate.tokenBalanceDeltas.length === 0) {
|
||||
return "-";
|
||||
}
|
||||
return candidate.tokenBalanceDeltas.slice(0, 4).map((delta) => {
|
||||
const amount = delta.deltaRaw ?? "?";
|
||||
return `${shortText(delta.mint, 10)}:${amount}`;
|
||||
}).join(", ");
|
||||
}
|
||||
|
||||
function appendLogLine(line: string): void {
|
||||
const textarea = byId<HTMLTextAreaElement>("demo3LogTextarea");
|
||||
const timestamp = new Date().toLocaleTimeString("fr-CH", { hour12: false });
|
||||
const lines = textarea.value === "" ? [] : textarea.value.split("\n");
|
||||
lines.push(`[${timestamp}] ${line}`);
|
||||
textarea.value = lines.slice(-400).join("\n");
|
||||
textarea.scrollTop = textarea.scrollHeight;
|
||||
}
|
||||
|
||||
function setStatus(label: string, cssClass: string): void {
|
||||
const badge = byId<HTMLElement>("demo3StatusBadge");
|
||||
badge.className = `badge ${cssClass}`;
|
||||
badge.textContent = label;
|
||||
}
|
||||
|
||||
function populatePresetSelect(): void {
|
||||
const select = byId<HTMLSelectElement>("demo3PresetSelect");
|
||||
select.innerHTML = '<option value="">Custom / empty</option>';
|
||||
presets.forEach((preset, index) => {
|
||||
const option = document.createElement("option");
|
||||
option.value = String(index);
|
||||
option.textContent = preset.label;
|
||||
select.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
function applyPreset(indexText: string): void {
|
||||
if (indexText === "") {
|
||||
return;
|
||||
}
|
||||
const index = Number.parseInt(indexText, 10);
|
||||
if (!Number.isFinite(index) || index < 0 || index >= presets.length) {
|
||||
return;
|
||||
}
|
||||
const preset = presets[index];
|
||||
byId<HTMLInputElement>("demo3DexCodeInput").value = preset.dexCode;
|
||||
byId<HTMLInputElement>("demo3ProgramIdInput").value = preset.programId;
|
||||
byId<HTMLElement>("demo3PresetHelp").textContent = preset.description;
|
||||
}
|
||||
|
||||
function readOnchainRequest(): Demo3OnchainDexDiscoveryRequest {
|
||||
return {
|
||||
dexCode: valueOrNull(byId<HTMLInputElement>("demo3DexCodeInput").value),
|
||||
programId: valueOrNull(byId<HTMLInputElement>("demo3ProgramIdInput").value),
|
||||
httpRole: byId<HTMLInputElement>("demo3HttpRoleInput").value.trim() || "history_backfill",
|
||||
signatureLimit: intValue("demo3SignatureLimitInput", 50),
|
||||
transactionLimit: intValue("demo3TransactionLimitInput", 25),
|
||||
candidateLimit: intValue("demo3CandidateLimitInput", 25),
|
||||
};
|
||||
}
|
||||
|
||||
function readLocalRequest(): Demo3LocalDexCorpusSearchRequest {
|
||||
return {
|
||||
dexCode: valueOrNull(byId<HTMLInputElement>("demo3DexCodeInput").value),
|
||||
programId: valueOrNull(byId<HTMLInputElement>("demo3ProgramIdInput").value),
|
||||
pairId: numberValueOrNull(byId<HTMLInputElement>("demo3PairIdInput").value),
|
||||
poolAddress: valueOrNull(byId<HTMLInputElement>("demo3PoolAddressInput").value),
|
||||
tokenMint: valueOrNull(byId<HTMLInputElement>("demo3TokenMintInput").value),
|
||||
signature: valueOrNull(byId<HTMLInputElement>("demo3SignatureInput").value),
|
||||
limit: intValue("demo3CandidateLimitInput", 25),
|
||||
};
|
||||
}
|
||||
|
||||
function clearFilters(): void {
|
||||
byId<HTMLInputElement>("demo3DexCodeInput").value = "";
|
||||
byId<HTMLInputElement>("demo3ProgramIdInput").value = "";
|
||||
byId<HTMLInputElement>("demo3PairIdInput").value = "";
|
||||
byId<HTMLInputElement>("demo3PoolAddressInput").value = "";
|
||||
byId<HTMLInputElement>("demo3TokenMintInput").value = "";
|
||||
byId<HTMLInputElement>("demo3SignatureInput").value = "";
|
||||
byId<HTMLSelectElement>("demo3PresetSelect").value = "";
|
||||
byId<HTMLElement>("demo3PresetHelp").textContent = "Choisis un DEX ou saisis un program id manuellement.";
|
||||
}
|
||||
|
||||
function renderOnchainResult(result: Demo3OnchainDexDiscoveryResult): void {
|
||||
byId<HTMLElement>("demo3SummarySignatureCount").textContent = String(result.fetchedSignatureCount);
|
||||
byId<HTMLElement>("demo3SummaryFetchedTxCount").textContent = String(result.fetchedTransactionCount);
|
||||
byId<HTMLElement>("demo3SummaryMissingTxCount").textContent = String(result.missingTransactionCount);
|
||||
byId<HTMLElement>("demo3SummaryFailedTxCount").textContent = String(result.failedTransactionCount);
|
||||
byId<HTMLElement>("demo3SummaryCandidateCount").textContent = String(result.candidateCount);
|
||||
byId<HTMLElement>("demo3TargetText").textContent = `${result.resolvedDexCode ?? "custom"} / ${result.resolvedProgramId}`;
|
||||
renderOnchainCandidates(result.candidates);
|
||||
}
|
||||
|
||||
function renderOnchainCandidates(candidates: Demo3OnchainDexPairCandidate[]): void {
|
||||
const body = byId<HTMLTableSectionElement>("demo3OnchainCandidateTableBody");
|
||||
if (candidates.length === 0) {
|
||||
body.innerHTML = '<tr><td colspan="11" class="text-body-secondary">No on-chain candidate.</td></tr>';
|
||||
return;
|
||||
}
|
||||
body.innerHTML = candidates.map((candidate) => {
|
||||
const verifiedPool = candidate.verifiedPoolAddress;
|
||||
const firstCandidatePool = candidate.candidatePoolAccounts.length > 0 ? candidate.candidatePoolAccounts[0].address : null;
|
||||
const poolForFilter = verifiedPool ?? candidate.poolAddress ?? firstCandidatePool;
|
||||
if (poolForFilter !== null) {
|
||||
byId<HTMLInputElement>("demo3PoolAddressInput").value = poolForFilter;
|
||||
}
|
||||
if (candidate.tokenAMint !== null && byId<HTMLInputElement>("demo3TokenMintInput").value.trim() === "") {
|
||||
byId<HTMLInputElement>("demo3TokenMintInput").value = candidate.tokenAMint;
|
||||
}
|
||||
if (candidate.tokenAMint === null && candidate.observedTokenMints.length > 0 && byId<HTMLInputElement>("demo3TokenMintInput").value.trim() === "") {
|
||||
byId<HTMLInputElement>("demo3TokenMintInput").value = candidate.observedTokenMints[0];
|
||||
}
|
||||
byId<HTMLInputElement>("demo3SignatureInput").value = candidate.signature;
|
||||
const accountTitle = candidate.candidatePoolAccounts.map((account) => `${account.address} (${account.reason})`).join("\n");
|
||||
const vaultTitle = candidate.candidateTokenVaultAccounts.map((account) => `${account.address} (${account.reason})`).join("\n");
|
||||
return `
|
||||
<tr>
|
||||
<td class="font-monospace" title="${escapeHtml(candidate.signature)}">${escapeHtml(shortText(candidate.signature, 18))}</td>
|
||||
<td>${candidate.slot ?? "-"}</td>
|
||||
<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(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>
|
||||
<td class="font-monospace" title="${escapeHtml(candidate.observedTokenMints.join("\n"))}">${escapeHtml(shortList(candidate.observedTokenMints, 3, 10))}</td>
|
||||
<td class="font-monospace" title="${escapeHtml(tokenDeltaList(candidate))}">${escapeHtml(tokenDeltaList(candidate))}</td>
|
||||
<td class="font-monospace" title="${escapeHtml(accountTitle)}">${escapeHtml(candidateAccountList(candidate.candidatePoolAccounts, 3))}<br /><span class="text-body-secondary">vaults: ${escapeHtml(candidateAccountList(candidate.candidateTokenVaultAccounts, 2))}</span></td>
|
||||
<td class="small" title="${escapeHtml(vaultTitle)}">${escapeHtml(candidate.backfillHint)}</td>
|
||||
</tr>`;
|
||||
}).join("");
|
||||
}
|
||||
|
||||
function renderLocalResult(result: Demo3LocalDexCorpusSearchResult): void {
|
||||
byId<HTMLElement>("demo3SummaryLocalPairCount").textContent = String(result.summary.pairCount);
|
||||
const body = byId<HTMLTableSectionElement>("demo3LocalPoolPairTableBody");
|
||||
if (result.poolPairSamples.length === 0) {
|
||||
body.innerHTML = '<tr><td colspan="8" class="text-body-secondary">No local pool/pair sample.</td></tr>';
|
||||
return;
|
||||
}
|
||||
body.innerHTML = result.poolPairSamples.map((sample) => `
|
||||
<tr>
|
||||
<td>${escapeHtml(sample.dexCode ?? "-")}</td>
|
||||
<td class="font-monospace" title="${escapeHtml(sample.poolAddress ?? "")}">${escapeHtml(shortText(sample.poolAddress, 16))}</td>
|
||||
<td>${sample.pairId ?? "-"}</td>
|
||||
<td>${escapeHtml(sample.pairSymbol ?? "-")}</td>
|
||||
<td class="font-monospace" title="${escapeHtml(sample.baseMint ?? "")}">${escapeHtml(sample.baseSymbol ?? shortText(sample.baseMint, 12))}</td>
|
||||
<td class="font-monospace" title="${escapeHtml(sample.quoteMint ?? "")}">${escapeHtml(sample.quoteSymbol ?? shortText(sample.quoteMint, 12))}</td>
|
||||
<td>${sample.tradeEventCount}</td>
|
||||
<td>${sample.pairCandleCount}</td>
|
||||
</tr>`).join("");
|
||||
}
|
||||
|
||||
async function discoverOnchain(): Promise<void> {
|
||||
const request = readOnchainRequest();
|
||||
setStatus("running", "text-bg-warning");
|
||||
appendLogLine(`on-chain discovery dex='${request.dexCode ?? ""}' program='${request.programId ?? ""}' 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}' signatures='${payload.result.fetchedSignatureCount}'`);
|
||||
} catch (error) {
|
||||
setStatus("error", "text-bg-danger");
|
||||
appendLogLine(`on-chain discovery failed: ${String(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function searchLocalDb(): Promise<void> {
|
||||
const request = readLocalRequest();
|
||||
setStatus("running", "text-bg-warning");
|
||||
appendLogLine("local DB search started");
|
||||
try {
|
||||
const payload = await invoke<Demo3LocalDexCorpusSearchPayload>("demo3_search_local_dex_corpus", { request });
|
||||
lastResultJson = payload.resultJson;
|
||||
byId<HTMLTextAreaElement>("demo3JsonTextarea").value = payload.resultJson;
|
||||
renderLocalResult(payload.result);
|
||||
setStatus("ok", "text-bg-success");
|
||||
appendLogLine(`local DB search completed: pairs='${payload.result.summary.pairCount}' tx='${payload.result.summary.transactionCount}'`);
|
||||
} catch (error) {
|
||||
setStatus("error", "text-bg-danger");
|
||||
appendLogLine(`local DB search failed: ${String(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function copyJson(): Promise<void> {
|
||||
if (lastResultJson === "") {
|
||||
appendLogLine("nothing to copy");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await navigator.clipboard.writeText(lastResultJson);
|
||||
appendLogLine("JSON copied to clipboard");
|
||||
} catch (error) {
|
||||
appendLogLine(`copy failed: ${String(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
function init(): void {
|
||||
takeoverConsole();
|
||||
void debug("demo3 on-chain discovery initialized");
|
||||
debug("demo3 window loaded");
|
||||
|
||||
const sidebarToggle = document.querySelector<HTMLButtonElement>('#sidebarToggle');
|
||||
if (sidebarToggle) {
|
||||
// restaurer l’état depuis localStorage
|
||||
if (localStorage.getItem('sidebar-toggle') === 'true') {
|
||||
document.body.classList.add('sidenav-toggled');
|
||||
}
|
||||
|
||||
sidebarToggle.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
document.body.classList.toggle('sidenav-toggled');
|
||||
localStorage.setItem('sidebar-toggle', document.body.classList.contains('sidenav-toggled') ? 'true' : 'false');
|
||||
});
|
||||
}
|
||||
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||
Array.from(tooltipTriggerList).map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
|
||||
const toastElList = document.querySelectorAll('.toast');
|
||||
Array.from(toastElList).map(toastEl => new bootstrap.Toast(toastEl));
|
||||
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
|
||||
Array.from(popoverTriggerList).map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl));
|
||||
|
||||
const gobackto = location.pathname + location.search;
|
||||
|
||||
document.querySelectorAll<HTMLAnchorElement>('a[data-setlang]').forEach((a) => {
|
||||
const href = a.getAttribute("href");
|
||||
if (!href) return; // pas de href => on ignore
|
||||
|
||||
const url = new URL(href, location.origin);
|
||||
url.searchParams.set("gobackto", gobackto);
|
||||
|
||||
// conserve une URL relative (path + query)
|
||||
a.setAttribute("href", url.pathname + "?" + url.searchParams.toString());
|
||||
});
|
||||
|
||||
populatePresetSelect();
|
||||
byId<HTMLSelectElement>("demo3PresetSelect").addEventListener("change", (event) => {
|
||||
applyPreset((event.target as HTMLSelectElement).value);
|
||||
});
|
||||
byId<HTMLButtonElement>("demo3DiscoverButton").addEventListener("click", () => {
|
||||
void discoverOnchain();
|
||||
});
|
||||
byId<HTMLButtonElement>("demo3LocalSearchButton").addEventListener("click", () => {
|
||||
void searchLocalDb();
|
||||
});
|
||||
byId<HTMLButtonElement>("demo3ClearFiltersButton").addEventListener("click", clearFilters);
|
||||
byId<HTMLButtonElement>("demo3CopyJsonButton").addEventListener("click", () => {
|
||||
void copyJson();
|
||||
});
|
||||
byId<HTMLButtonElement>("demo3ClearLogButton").addEventListener("click", () => {
|
||||
byId<HTMLTextAreaElement>("demo3LogTextarea").value = "";
|
||||
});
|
||||
appendLogLine("ready");
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
435
kb_demo_app/frontend/ts/demo3old.ts
Normal file
435
kb_demo_app/frontend/ts/demo3old.ts
Normal file
@@ -0,0 +1,435 @@
|
||||
// file: kb_demo_app/frontend/ts/demo3old.ts
|
||||
|
||||
import * as bootstrap from "bootstrap";
|
||||
import "simplebar";
|
||||
import ResizeObserver from "resize-observer-polyfill";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { debug, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
|
||||
import type { Demo3oldLocalDexCorpusSearchRequest } from "./bindings/Demo3oldLocalDexCorpusSearchRequest.ts";
|
||||
import type { Demo3oldLocalDexCorpusSearchPayload } from "./bindings/Demo3oldLocalDexCorpusSearchPayload.ts";
|
||||
import type { Demo3oldLocalDexCorpusSearchSummary } from "./bindings/Demo3oldLocalDexCorpusSearchSummary.ts";
|
||||
import type { Demo3oldLocalDexCorpusTransactionSample } from "./bindings/Demo3oldLocalDexCorpusTransactionSample.ts";
|
||||
import type { Demo3oldLocalDexCorpusPoolPairSample } from "./bindings/Demo3oldLocalDexCorpusPoolPairSample.ts";
|
||||
import type { Demo3oldLocalDexCorpusDecodedEventSample } from "./bindings/Demo3oldLocalDexCorpusDecodedEventSample.ts";
|
||||
|
||||
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
||||
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
||||
|
||||
|
||||
|
||||
interface Demo3oldPreset {
|
||||
label: string;
|
||||
dexCode: string;
|
||||
programId: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const presets: Demo3oldPreset[] = [
|
||||
{
|
||||
label: "PumpSwap",
|
||||
dexCode: "pump_swap",
|
||||
programId: "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA",
|
||||
description: "DEX effectif PumpSwap.",
|
||||
},
|
||||
{
|
||||
label: "Raydium CPMM",
|
||||
dexCode: "raydium_cpmm",
|
||||
programId: "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C",
|
||||
description: "Raydium CPMM, swap AMM moderne.",
|
||||
},
|
||||
{
|
||||
label: "Raydium CLMM",
|
||||
dexCode: "raydium_clmm",
|
||||
programId: "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK",
|
||||
description: "Raydium CLMM, actuellement observé dans le corpus local.",
|
||||
},
|
||||
{
|
||||
label: "Raydium AMM v4",
|
||||
dexCode: "raydium_amm_v4",
|
||||
programId: "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8",
|
||||
description: "Raydium AMM v4 legacy. À rechercher par corpus avant décodage swap.",
|
||||
},
|
||||
{
|
||||
label: "Raydium Stable Swap",
|
||||
dexCode: "raydium_stable_swap",
|
||||
programId: "5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h",
|
||||
description: "Stable Swap Raydium à vérifier par transactions locales.",
|
||||
},
|
||||
{
|
||||
label: "Raydium Router",
|
||||
dexCode: "raydium_router",
|
||||
programId: "routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS",
|
||||
description: "Router Raydium. Ne doit pas être promu comme DEX direct sans preuve.",
|
||||
},
|
||||
{
|
||||
label: "Meteora DLMM",
|
||||
dexCode: "meteora_dlmm",
|
||||
programId: "LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo",
|
||||
description: "Meteora DLMM.",
|
||||
},
|
||||
{
|
||||
label: "Meteora DAMM v1",
|
||||
dexCode: "meteora_damm_v1",
|
||||
programId: "Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB",
|
||||
description: "Meteora DAMM v1, swaps non actionnables sans montants exploitables.",
|
||||
},
|
||||
{
|
||||
label: "Meteora DAMM v2",
|
||||
dexCode: "meteora_damm_v2",
|
||||
programId: "cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG",
|
||||
description: "Meteora DAMM v2.",
|
||||
},
|
||||
{
|
||||
label: "Meteora DBC",
|
||||
dexCode: "meteora_dbc",
|
||||
programId: "dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN",
|
||||
description: "Meteora DBC, bonding curve / DEX effectif partiel.",
|
||||
},
|
||||
{
|
||||
label: "Orca Whirlpools",
|
||||
dexCode: "orca_whirlpools",
|
||||
programId: "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc",
|
||||
description: "Orca Whirlpools CLMM.",
|
||||
},
|
||||
{
|
||||
label: "FluxBeam",
|
||||
dexCode: "fluxbeam",
|
||||
programId: "FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X",
|
||||
description: "FluxBeam AMM à consolider par corpus.",
|
||||
},
|
||||
{
|
||||
label: "DexLab",
|
||||
dexCode: "dexlab",
|
||||
programId: "DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N",
|
||||
description: "DexLab Swap/Pool à consolider par corpus.",
|
||||
},
|
||||
{
|
||||
label: "metaDAO",
|
||||
dexCode: "metadao",
|
||||
programId: "",
|
||||
description: "DEX à vérifier. Aucun program id ne doit être inventé.",
|
||||
},
|
||||
{
|
||||
label: "Printr",
|
||||
dexCode: "printr",
|
||||
programId: "",
|
||||
description: "DEX à vérifier. Aucun program id ne doit être inventé.",
|
||||
},
|
||||
];
|
||||
|
||||
let lastResultJson = "";
|
||||
|
||||
function byId<T extends HTMLElement>(id: string): T {
|
||||
const element = document.getElementById(id);
|
||||
if (element === null) {
|
||||
throw new Error(`missing element #${id}`);
|
||||
}
|
||||
return element as T;
|
||||
}
|
||||
|
||||
function valueOrNull(value: string): string | null {
|
||||
const trimmed = value.trim();
|
||||
if (trimmed === "") {
|
||||
return null;
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
function numberValueOrNull(value: string): number | null {
|
||||
const trimmed = value.trim();
|
||||
if (trimmed === "") {
|
||||
return null;
|
||||
}
|
||||
const parsed = Number.parseInt(trimmed, 10);
|
||||
if (!Number.isFinite(parsed)) {
|
||||
return null;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function displayNullable(value: string | number | null): string {
|
||||
if (value === null) {
|
||||
return "-";
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function boolBadge(value: boolean): string {
|
||||
if (value) {
|
||||
return '<span class="badge text-bg-success">yes</span>';
|
||||
}
|
||||
return '<span class="badge text-bg-secondary">no</span>';
|
||||
}
|
||||
|
||||
function escapeHtml(value: string): string {
|
||||
return value
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
function shortText(value: string | null, maxLength: number): string {
|
||||
if (value === null) {
|
||||
return "-";
|
||||
}
|
||||
if (value.length <= maxLength) {
|
||||
return value;
|
||||
}
|
||||
return `${value.slice(0, maxLength)}…`;
|
||||
}
|
||||
|
||||
function appendLogLine(textarea: HTMLTextAreaElement, line: string): void {
|
||||
const now = new Date();
|
||||
const timestamp = now.toLocaleTimeString("fr-CH", { hour12: false });
|
||||
const lines = textarea.value === "" ? [] : textarea.value.split("\n");
|
||||
lines.push(`[${timestamp}] ${line}`);
|
||||
textarea.value = lines.slice(-400).join("\n");
|
||||
textarea.scrollTop = textarea.scrollHeight;
|
||||
}
|
||||
|
||||
function setStatus(statusBadge: HTMLElement, label: string, cssClass: string): void {
|
||||
statusBadge.className = `badge ${cssClass}`;
|
||||
statusBadge.textContent = label;
|
||||
}
|
||||
|
||||
function readRequest(): Demo3oldLocalDexCorpusSearchRequest {
|
||||
const limitInput = byId<HTMLInputElement>("demo3oldLimitInput");
|
||||
const parsedLimit = Number.parseInt(limitInput.value, 10);
|
||||
const limit = Number.isFinite(parsedLimit) && parsedLimit > 0 ? parsedLimit : 50;
|
||||
return {
|
||||
dexCode: valueOrNull(byId<HTMLInputElement>("demo3oldDexCodeInput").value),
|
||||
programId: valueOrNull(byId<HTMLInputElement>("demo3oldProgramIdInput").value),
|
||||
pairId: numberValueOrNull(byId<HTMLInputElement>("demo3oldPairIdInput").value),
|
||||
poolAddress: valueOrNull(byId<HTMLInputElement>("demo3oldPoolAddressInput").value),
|
||||
tokenMint: valueOrNull(byId<HTMLInputElement>("demo3oldTokenMintInput").value),
|
||||
signature: valueOrNull(byId<HTMLInputElement>("demo3oldSignatureInput").value),
|
||||
limit,
|
||||
};
|
||||
}
|
||||
|
||||
function populatePresetSelect(select: HTMLSelectElement): void {
|
||||
select.innerHTML = '<option value="">Custom / empty</option>';
|
||||
presets.forEach((preset, index) => {
|
||||
const option = document.createElement("option");
|
||||
option.value = String(index);
|
||||
option.textContent = preset.label;
|
||||
select.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
function applyPreset(indexText: string): void {
|
||||
if (indexText === "") {
|
||||
return;
|
||||
}
|
||||
const index = Number.parseInt(indexText, 10);
|
||||
if (!Number.isFinite(index) || index < 0 || index >= presets.length) {
|
||||
return;
|
||||
}
|
||||
const preset = presets[index];
|
||||
byId<HTMLInputElement>("demo3oldDexCodeInput").value = preset.dexCode;
|
||||
byId<HTMLInputElement>("demo3oldProgramIdInput").value = preset.programId;
|
||||
byId<HTMLElement>("demo3oldPresetHelp").textContent = preset.description;
|
||||
}
|
||||
|
||||
function clearFilters(): void {
|
||||
byId<HTMLInputElement>("demo3oldDexCodeInput").value = "";
|
||||
byId<HTMLInputElement>("demo3oldProgramIdInput").value = "";
|
||||
byId<HTMLInputElement>("demo3oldPairIdInput").value = "";
|
||||
byId<HTMLInputElement>("demo3oldPoolAddressInput").value = "";
|
||||
byId<HTMLInputElement>("demo3oldTokenMintInput").value = "";
|
||||
byId<HTMLInputElement>("demo3oldSignatureInput").value = "";
|
||||
byId<HTMLSelectElement>("demo3oldPresetSelect").value = "";
|
||||
byId<HTMLElement>("demo3oldPresetHelp").textContent = "Choisis un preset ou saisis les filtres manuellement.";
|
||||
}
|
||||
|
||||
function renderSummary(summary: Demo3oldLocalDexCorpusSearchSummary): void {
|
||||
byId<HTMLElement>("demo3oldSummaryTransactionCount").textContent = String(summary.transactionCount);
|
||||
byId<HTMLElement>("demo3oldSummaryInstructionCount").textContent = String(summary.instructionCount);
|
||||
byId<HTMLElement>("demo3oldSummaryDecodedEventCount").textContent = String(summary.decodedEventCount);
|
||||
byId<HTMLElement>("demo3oldSummaryPoolCount").textContent = String(summary.poolCount);
|
||||
byId<HTMLElement>("demo3oldSummaryPairCount").textContent = String(summary.pairCount);
|
||||
byId<HTMLElement>("demo3oldSummaryTradeEventCount").textContent = String(summary.tradeEventCount);
|
||||
byId<HTMLElement>("demo3oldSummaryCandleCount").textContent = String(summary.pairCandleCount);
|
||||
byId<HTMLElement>("demo3oldSummaryProtocolCandidateCount").textContent = String(summary.protocolCandidateCount);
|
||||
}
|
||||
|
||||
function renderTransactionSamples(samples: Demo3oldLocalDexCorpusTransactionSample[]): void {
|
||||
const tbody = byId<HTMLTableSectionElement>("demo3oldTransactionTableBody");
|
||||
tbody.innerHTML = "";
|
||||
if (samples.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="8" class="text-body-secondary">No transaction sample.</td></tr>';
|
||||
return;
|
||||
}
|
||||
samples.forEach((sample) => {
|
||||
const tr = document.createElement("tr");
|
||||
tr.innerHTML = `
|
||||
<td class="font-monospace" title="${escapeHtml(sample.signature)}">${escapeHtml(shortText(sample.signature, 18))}</td>
|
||||
<td>${displayNullable(sample.slot)}</td>
|
||||
<td>${boolBadge(sample.failed)}</td>
|
||||
<td>${sample.instructionCount}</td>
|
||||
<td>${sample.decodedEventCount}</td>
|
||||
<td>${sample.tradeEventCount}</td>
|
||||
<td class="font-monospace" title="${escapeHtml(sample.programIdsCsv)}">${escapeHtml(shortText(sample.programIdsCsv, 44))}</td>
|
||||
<td>${sample.transactionId}</td>
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
function renderPoolPairSamples(samples: Demo3oldLocalDexCorpusPoolPairSample[]): void {
|
||||
const tbody = byId<HTMLTableSectionElement>("demo3oldPoolPairTableBody");
|
||||
tbody.innerHTML = "";
|
||||
if (samples.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="9" class="text-body-secondary">No pool/pair sample.</td></tr>';
|
||||
return;
|
||||
}
|
||||
samples.forEach((sample) => {
|
||||
const baseLabel = `${displayNullable(sample.baseSymbol)} / ${shortText(sample.baseMint, 12)}`;
|
||||
const quoteLabel = `${displayNullable(sample.quoteSymbol)} / ${shortText(sample.quoteMint, 12)}`;
|
||||
const tr = document.createElement("tr");
|
||||
tr.innerHTML = `
|
||||
<td>${displayNullable(sample.dexCode)}</td>
|
||||
<td title="${escapeHtml(displayNullable(sample.poolAddress))}" class="font-monospace">${escapeHtml(shortText(sample.poolAddress, 16))}</td>
|
||||
<td>${displayNullable(sample.pairId)}</td>
|
||||
<td>${escapeHtml(displayNullable(sample.pairSymbol))}</td>
|
||||
<td class="font-monospace" title="${escapeHtml(displayNullable(sample.baseMint))}">${escapeHtml(baseLabel)}</td>
|
||||
<td class="font-monospace" title="${escapeHtml(displayNullable(sample.quoteMint))}">${escapeHtml(quoteLabel)}</td>
|
||||
<td>${sample.decodedEventCount}</td>
|
||||
<td>${sample.tradeEventCount}</td>
|
||||
<td>${sample.pairCandleCount}</td>
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
function renderDecodedEventSamples(samples: Demo3oldLocalDexCorpusDecodedEventSample[]): void {
|
||||
const tbody = byId<HTMLTableSectionElement>("demo3oldDecodedEventTableBody");
|
||||
tbody.innerHTML = "";
|
||||
if (samples.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="11" class="text-body-secondary">No decoded event sample.</td></tr>';
|
||||
return;
|
||||
}
|
||||
samples.forEach((sample) => {
|
||||
const tr = document.createElement("tr");
|
||||
tr.innerHTML = `
|
||||
<td>${sample.decodedEventId}</td>
|
||||
<td class="font-monospace" title="${escapeHtml(sample.signature)}">${escapeHtml(shortText(sample.signature, 16))}</td>
|
||||
<td>${displayNullable(sample.slot)}</td>
|
||||
<td>${escapeHtml(sample.protocolName)}</td>
|
||||
<td>${escapeHtml(sample.eventKind)}</td>
|
||||
<td class="font-monospace" title="${escapeHtml(sample.programId)}">${escapeHtml(shortText(sample.programId, 16))}</td>
|
||||
<td class="font-monospace" title="${escapeHtml(displayNullable(sample.poolAccount))}">${escapeHtml(shortText(sample.poolAccount, 16))}</td>
|
||||
<td>${escapeHtml(displayNullable(sample.eventCategory))}</td>
|
||||
<td>${escapeHtml(displayNullable(sample.eventActionability))}</td>
|
||||
<td>${boolBadge(sample.tradeCandidate)}</td>
|
||||
<td>${boolBadge(sample.candleCandidate)}</td>
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
function renderResult(payload: Demo3oldLocalDexCorpusSearchPayload): void {
|
||||
lastResultJson = payload.resultJson;
|
||||
byId<HTMLElement>("demo3oldDatabaseUrlText").textContent = payload.databaseUrl;
|
||||
byId<HTMLTextAreaElement>("demo3oldJsonTextarea").value = payload.resultJson;
|
||||
renderSummary(payload.result.summary);
|
||||
renderTransactionSamples(payload.result.transactionSamples);
|
||||
renderPoolPairSamples(payload.result.poolPairSamples);
|
||||
renderDecodedEventSamples(payload.result.decodedEventSamples);
|
||||
}
|
||||
|
||||
async function runSearch(): Promise<void> {
|
||||
const statusBadge = byId<HTMLElement>("demo3oldStatusBadge");
|
||||
const logTextarea = byId<HTMLTextAreaElement>("demo3oldLogTextarea");
|
||||
setStatus(statusBadge, "Searching", "text-bg-warning");
|
||||
appendLogLine(logTextarea, "launching local DEX corpus search");
|
||||
try {
|
||||
const request = readRequest();
|
||||
const payload = await invoke<Demo3oldLocalDexCorpusSearchPayload>("demo3old_search_local_dex_corpus", { request });
|
||||
renderResult(payload);
|
||||
appendLogLine(
|
||||
logTextarea,
|
||||
`search completed: tx='${payload.result.summary.transactionCount}', pools='${payload.result.summary.poolCount}', pairs='${payload.result.summary.pairCount}', decoded='${payload.result.summary.decodedEventCount}'`,
|
||||
);
|
||||
setStatus(statusBadge, "Ready", "text-bg-success");
|
||||
} catch (error) {
|
||||
appendLogLine(logTextarea, `search failed: ${String(error)}`);
|
||||
setStatus(statusBadge, "Error", "text-bg-danger");
|
||||
}
|
||||
}
|
||||
|
||||
async function copyJson(): Promise<void> {
|
||||
const logTextarea = byId<HTMLTextAreaElement>("demo3oldLogTextarea");
|
||||
if (lastResultJson === "") {
|
||||
appendLogLine(logTextarea, "no JSON result to copy");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await navigator.clipboard.writeText(lastResultJson);
|
||||
appendLogLine(logTextarea, "JSON result copied");
|
||||
} catch (error) {
|
||||
appendLogLine(logTextarea, `copy failed: ${String(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
void takeoverConsole();
|
||||
debug("demo3old window loaded");
|
||||
|
||||
const sidebarToggle = document.querySelector<HTMLButtonElement>('#sidebarToggle');
|
||||
if (sidebarToggle) {
|
||||
// restaurer l’état depuis localStorage
|
||||
if (localStorage.getItem('sidebar-toggle') === 'true') {
|
||||
document.body.classList.add('sidenav-toggled');
|
||||
}
|
||||
|
||||
sidebarToggle.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
document.body.classList.toggle('sidenav-toggled');
|
||||
localStorage.setItem('sidebar-toggle', document.body.classList.contains('sidenav-toggled') ? 'true' : 'false');
|
||||
});
|
||||
}
|
||||
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||
Array.from(tooltipTriggerList).map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
|
||||
const toastElList = document.querySelectorAll('.toast');
|
||||
Array.from(toastElList).map(toastEl => new bootstrap.Toast(toastEl));
|
||||
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
|
||||
Array.from(popoverTriggerList).map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl));
|
||||
|
||||
const gobackto = location.pathname + location.search;
|
||||
|
||||
document.querySelectorAll<HTMLAnchorElement>('a[data-setlang]').forEach((a) => {
|
||||
const href = a.getAttribute("href");
|
||||
if (!href) return; // pas de href => on ignore
|
||||
|
||||
const url = new URL(href, location.origin);
|
||||
url.searchParams.set("gobackto", gobackto);
|
||||
|
||||
// conserve une URL relative (path + query)
|
||||
a.setAttribute("href", url.pathname + "?" + url.searchParams.toString());
|
||||
});
|
||||
|
||||
const presetSelect = byId<HTMLSelectElement>("demo3oldPresetSelect");
|
||||
populatePresetSelect(presetSelect);
|
||||
presetSelect.addEventListener("change", () => {
|
||||
applyPreset(presetSelect.value);
|
||||
});
|
||||
|
||||
byId<HTMLButtonElement>("demo3oldSearchButton").addEventListener("click", () => {
|
||||
void runSearch();
|
||||
});
|
||||
byId<HTMLButtonElement>("demo3oldClearFiltersButton").addEventListener("click", () => {
|
||||
clearFilters();
|
||||
});
|
||||
byId<HTMLButtonElement>("demo3oldCopyJsonButton").addEventListener("click", () => {
|
||||
void copyJson();
|
||||
});
|
||||
byId<HTMLButtonElement>("demo3oldClearLogButton").addEventListener("click", () => {
|
||||
byId<HTMLTextAreaElement>("demo3oldLogTextarea").value = "";
|
||||
});
|
||||
|
||||
applyPreset("3");
|
||||
});
|
||||
@@ -10,6 +10,7 @@ import { debug, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
|
||||
import type { DemoPipeline2CatalogPayload } from "./bindings/DemoPipeline2CatalogPayload.ts";
|
||||
import type { DemoPipeline2BackfillTokenRequest } from "./bindings/DemoPipeline2BackfillTokenRequest.ts";
|
||||
import type { DemoPipeline2BackfillPoolRequest } from "./bindings/DemoPipeline2BackfillPoolRequest.ts";
|
||||
import type { DemoPipeline2BackfillSignatureRequest } from "./bindings/DemoPipeline2BackfillSignatureRequest.ts";
|
||||
import type { DemoPipeline2BackfillPayload } from "./bindings/DemoPipeline2BackfillPayload.ts";
|
||||
import type { DemoPipeline2PairCandlesRequest } from "./bindings/DemoPipeline2PairCandlesRequest.ts";
|
||||
import type { DemoPipeline2PairCandlesPayload } from "./bindings/DemoPipeline2PairCandlesPayload.ts";
|
||||
@@ -358,6 +359,9 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
const poolSignatureLimitInput = document.querySelector<HTMLInputElement>("#demoPipeline2PoolSignatureLimitInput");
|
||||
const backfillPoolButton = document.querySelector<HTMLButtonElement>("#demoPipeline2BackfillPoolButton");
|
||||
|
||||
const signatureInput = document.querySelector<HTMLInputElement>("#demoPipeline2SignatureInput");
|
||||
const backfillSignatureButton = document.querySelector<HTMLButtonElement>("#demoPipeline2BackfillSignatureButton");
|
||||
|
||||
const replayLimitInput = document.querySelector<HTMLInputElement>("#demoPipeline2ReplayLimitInput");
|
||||
const replayMetadataCheckbox = document.querySelector<HTMLInputElement>("#demoPipeline2ReplayMetadataCheckbox");
|
||||
const replayMetadataLimitInput = document.querySelector<HTMLInputElement>("#demoPipeline2ReplayMetadataLimitInput");
|
||||
@@ -405,6 +409,8 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
!poolInput ||
|
||||
!poolSignatureLimitInput ||
|
||||
!backfillPoolButton ||
|
||||
!signatureInput ||
|
||||
!backfillSignatureButton ||
|
||||
!replayLimitInput ||
|
||||
!replayMetadataCheckbox ||
|
||||
!replayMetadataLimitInput ||
|
||||
@@ -585,6 +591,43 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
}
|
||||
});
|
||||
|
||||
backfillSignatureButton.addEventListener("click", async () => {
|
||||
const signature = signatureInput.value.trim();
|
||||
if (signature === "") {
|
||||
appendLogLine(logTextarea, "[ui] signature is required");
|
||||
return;
|
||||
}
|
||||
|
||||
const httpRoleText = httpRoleInput.value.trim();
|
||||
const httpRole = httpRoleText === "" ? null : httpRoleText;
|
||||
|
||||
appendLogLine(
|
||||
logTextarea,
|
||||
`[ui] launching signature backfill for '${signature}' with role '${httpRole ?? "history_backfill"}'`,
|
||||
);
|
||||
|
||||
const request: DemoPipeline2BackfillSignatureRequest = {
|
||||
signature,
|
||||
httpRole,
|
||||
};
|
||||
|
||||
try {
|
||||
const payload = await invoke<DemoPipeline2BackfillPayload>(
|
||||
"demo_pipeline2_backfill_signature",
|
||||
{ request },
|
||||
);
|
||||
|
||||
backfillSummaryTextarea.value = payload.summaryJson;
|
||||
currentCatalog = payload.catalog;
|
||||
renderCatalogTextareas(payload.catalog, tokensTextarea, poolsTextarea, pairsTextarea);
|
||||
refreshPairSelect(payload.catalog, pairSelect);
|
||||
|
||||
appendLogLine(logTextarea, `[ui] signature backfill completed for '${payload.objectKey}'`);
|
||||
} catch (error) {
|
||||
appendLogLine(logTextarea, `[ui] signature backfill error: ${String(error)}`);
|
||||
}
|
||||
});
|
||||
|
||||
replayLocalPipelineButton.addEventListener("click", async () => {
|
||||
const replayLimit = readOptionalPositiveIntegerInput(
|
||||
replayLimitInput,
|
||||
|
||||
@@ -32,6 +32,24 @@ async function openDemoWsManagerWindow(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function openDemo3Window(): Promise<void> {
|
||||
try {
|
||||
await invoke("open_demo3_window");
|
||||
} catch (error) {
|
||||
console.error("open_demo3_window failed:", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function openDemo3oldWindow(): Promise<void> {
|
||||
try {
|
||||
await invoke("open_demo3old_window");
|
||||
} catch (error) {
|
||||
console.error("open_demo3old_window failed:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function openDemoPipelineWindow(): Promise<void> {
|
||||
try {
|
||||
await invoke("open_demo_pipeline_window");
|
||||
@@ -93,6 +111,10 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
const openDemoHttpButtonSecondary = document.querySelector<HTMLButtonElement>("#openDemoHttpButtonSecondary");
|
||||
const openDemoWsManagerButton = document.querySelector<HTMLButtonElement>("#openDemoWsManagerButton");
|
||||
const openDemoWsManagerButtonSecondary = document.querySelector<HTMLButtonElement>("#openDemoWsManagerButtonSecondary");
|
||||
const openDemo3Button = document.querySelector<HTMLButtonElement>("#openDemo3Button");
|
||||
const openDemo3ButtonSecondary = document.querySelector<HTMLButtonElement>("#openDemo3ButtonSecondary");
|
||||
const openDemo3oldButton = document.querySelector<HTMLButtonElement>("#openDemo3oldButton");
|
||||
const openDemo3oldButtonSecondary = document.querySelector<HTMLButtonElement>("#openDemo3oldButtonSecondary");
|
||||
const openDemoPipelineButton = document.querySelector<HTMLButtonElement>("#openDemoPipelineButton");
|
||||
const openDemoPipelineButtonSecondary = document.querySelector<HTMLButtonElement>("#openDemoPipelineButtonSecondary");
|
||||
const openDemoPipeline2Button = document.querySelector<HTMLButtonElement>("#openDemoPipeline2Button");
|
||||
@@ -134,6 +156,30 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
});
|
||||
}
|
||||
|
||||
if (openDemo3Button) {
|
||||
openDemo3Button.addEventListener("click", () => {
|
||||
void openDemo3Window();
|
||||
});
|
||||
}
|
||||
|
||||
if (openDemo3ButtonSecondary) {
|
||||
openDemo3ButtonSecondary.addEventListener("click", () => {
|
||||
void openDemo3Window();
|
||||
});
|
||||
}
|
||||
|
||||
if (openDemo3oldButton) {
|
||||
openDemo3oldButton.addEventListener("click", () => {
|
||||
void openDemo3oldWindow();
|
||||
});
|
||||
}
|
||||
|
||||
if (openDemo3oldButtonSecondary) {
|
||||
openDemo3oldButtonSecondary.addEventListener("click", () => {
|
||||
void openDemo3oldWindow();
|
||||
});
|
||||
}
|
||||
|
||||
if (openDemoPipelineButton) {
|
||||
openDemoPipelineButton.addEventListener("click", () => {
|
||||
void openDemoPipelineWindow();
|
||||
|
||||
Reference in New Issue
Block a user