-
Notifications
You must be signed in to change notification settings - Fork 1
feat: view transaction ledger query action #135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
5cbde57
feat: view transaction ledger query action
MicBun db4eed2
chore: apply suggestion
MicBun 76c81e2
chore: apply suggestion
MicBun 205e923
chore: apply suggestion
MicBun 0c0c0a5
chore: skip attestation test on CI
MicBun 60aa568
Merge branch 'main' into fetchHistory
MicBun File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| import { KwilSigner, NodeKwil, WebKwil, Types } from "@trufnetwork/kwil-js"; | ||
| import { TransactionEvent, FeeDistribution, GetTransactionEventInput } from "../types/transaction"; | ||
|
|
||
| /** | ||
| * Database row structure returned from get_transaction_event action | ||
| */ | ||
| interface TransactionEventRow { | ||
| tx_id: string; | ||
| block_height: string | number; | ||
| method: string; | ||
| caller: string; | ||
| fee_amount: string | number; | ||
| fee_recipient?: string | null; | ||
| metadata?: string | null; | ||
| fee_distributions: string; | ||
| } | ||
|
|
||
| /** | ||
| * TransactionAction provides methods for querying transaction ledger data | ||
| */ | ||
| export class TransactionAction { | ||
| protected kwilClient: WebKwil | NodeKwil; | ||
| protected kwilSigner: KwilSigner; | ||
|
|
||
| constructor(kwilClient: WebKwil | NodeKwil, kwilSigner: KwilSigner) { | ||
| this.kwilClient = kwilClient; | ||
| this.kwilSigner = kwilSigner; | ||
| } | ||
|
|
||
| /** | ||
| * Fetches detailed transaction information by transaction hash | ||
| * | ||
| * @param input Transaction query input containing tx hash | ||
| * @returns Promise resolving to transaction event with fee details | ||
| * @throws Error if transaction not found or query fails | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const txAction = client.loadTransactionAction(); | ||
| * const txEvent = await txAction.getTransactionEvent({ | ||
| * txId: "0xabcdef123456..." | ||
| * }); | ||
| * console.log(`Method: ${txEvent.method}, Fee: ${txEvent.feeAmount} TRUF`); | ||
| * ``` | ||
| */ | ||
| async getTransactionEvent(input: GetTransactionEventInput): Promise<TransactionEvent> { | ||
| if (!input.txId || input.txId.trim() === "") { | ||
| throw new Error("tx_id is required"); | ||
| } | ||
|
|
||
| const result = await this.kwilClient.call( | ||
| { | ||
| namespace: "main", | ||
| name: "get_transaction_event", | ||
| inputs: { | ||
| $tx_id: input.txId, | ||
| }, | ||
| }, | ||
| this.kwilSigner | ||
| ); | ||
|
|
||
| if (result.status !== 200) { | ||
| throw new Error(`Failed to get transaction event: HTTP ${result.status}`); | ||
| } | ||
|
|
||
| if (!result.data?.result || result.data.result.length === 0) { | ||
| throw new Error(`Transaction not found: ${input.txId}`); | ||
| } | ||
|
|
||
| const row = result.data.result[0] as TransactionEventRow; | ||
|
|
||
| // Validate required fields | ||
| if (!row.method || typeof row.method !== 'string' || row.method.trim() === '') { | ||
| throw new Error(`Missing or invalid method field (tx: ${row.tx_id})`); | ||
| } | ||
|
|
||
| if (!row.caller || typeof row.caller !== 'string' || row.caller.trim() === '') { | ||
| throw new Error(`Missing or invalid caller field (tx: ${row.tx_id})`); | ||
| } | ||
|
|
||
| if (row.fee_amount === null || row.fee_amount === undefined) { | ||
| throw new Error(`Missing fee_amount field (tx: ${row.tx_id})`); | ||
| } | ||
|
|
||
| // Validate fee_amount is numeric (can be string or number) | ||
| const feeAmount = typeof row.fee_amount === 'string' ? row.fee_amount : String(row.fee_amount); | ||
| const feeAmountNum = Number(feeAmount); | ||
| if (isNaN(feeAmountNum) || !Number.isFinite(feeAmountNum)) { | ||
| throw new Error(`Invalid fee_amount (not numeric): ${row.fee_amount} (tx: ${row.tx_id})`); | ||
| } | ||
| if (feeAmountNum < 0) { | ||
| throw new Error(`Invalid fee_amount (negative): ${row.fee_amount} (tx: ${row.tx_id})`); | ||
| } | ||
|
|
||
| // Parse fee_distributions string: "recipient1:amount1,recipient2:amount2" | ||
| const feeDistributions: FeeDistribution[] = []; | ||
| if (row.fee_distributions && row.fee_distributions !== "") { | ||
| const parts = row.fee_distributions.split(","); | ||
| for (const part of parts) { | ||
| const trimmedPart = part.trim(); | ||
| if (trimmedPart) { | ||
| // Split only on first colon to handle addresses with colons | ||
| const colonIndex = trimmedPart.indexOf(":"); | ||
| if (colonIndex === -1) { | ||
| throw new Error(`Invalid fee distribution format (missing colon): ${trimmedPart} (tx: ${row.tx_id})`); | ||
| } | ||
|
|
||
| const recipient = trimmedPart.substring(0, colonIndex).trim(); | ||
| const amount = trimmedPart.substring(colonIndex + 1).trim(); | ||
|
|
||
| if (!recipient || !amount) { | ||
| throw new Error(`Invalid fee distribution entry (empty recipient or amount): ${trimmedPart} (tx: ${row.tx_id})`); | ||
| } | ||
|
|
||
| // Validate amount is numeric and non-negative | ||
| const amt = Number(amount); | ||
| if (isNaN(amt) || !Number.isFinite(amt)) { | ||
| throw new Error(`Invalid fee distribution amount (not numeric): ${amount} (tx: ${row.tx_id})`); | ||
| } | ||
| if (amt < 0) { | ||
| throw new Error(`Invalid fee distribution amount (negative): ${amount} (tx: ${row.tx_id})`); | ||
| } | ||
|
|
||
| feeDistributions.push({ recipient, amount }); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Validate block height | ||
| const blockHeight = typeof row.block_height === 'number' | ||
| ? row.block_height | ||
| : parseInt(row.block_height, 10); | ||
| if (!Number.isFinite(blockHeight) || blockHeight < 0) { | ||
| throw new Error(`Invalid block height: ${row.block_height} (tx: ${row.tx_id})`); | ||
| } | ||
|
|
||
| return { | ||
| txId: row.tx_id, | ||
| blockHeight, | ||
| method: row.method, | ||
| caller: row.caller, | ||
| feeAmount, | ||
| feeRecipient: row.fee_recipient || undefined, | ||
| metadata: row.metadata || undefined, | ||
| feeDistributions, | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Creates a TransactionAction instance from an existing client and signer | ||
| * | ||
| * @param kwilClient The Kwil client (Web or Node) | ||
| * @param kwilSigner The Kwil signer for authentication | ||
| * @returns A new TransactionAction instance | ||
| */ | ||
| static fromClient(kwilClient: WebKwil | NodeKwil, kwilSigner: KwilSigner): TransactionAction { | ||
| return new TransactionAction(kwilClient, kwilSigner); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.