@trufnetwork/sdk-js 0.5.3 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -1
- package/dist/cjs/contracts-api/transactionAction.cjs +28 -0
- package/dist/cjs/contracts-api/transactionAction.cjs.map +2 -2
- package/dist/cjs/internal.cjs +2 -0
- package/dist/cjs/internal.cjs.map +2 -2
- package/dist/cjs/types/transaction.cjs.map +1 -1
- package/dist/cjs/util/AttestationEncoding.cjs +341 -0
- package/dist/cjs/util/AttestationEncoding.cjs.map +2 -2
- package/dist/esm/contracts-api/transactionAction.mjs +28 -0
- package/dist/esm/contracts-api/transactionAction.mjs.map +2 -2
- package/dist/esm/internal.mjs +4 -0
- package/dist/esm/internal.mjs.map +2 -2
- package/dist/esm/util/AttestationEncoding.mjs +341 -0
- package/dist/esm/util/AttestationEncoding.mjs.map +2 -2
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/contracts-api/transactionAction.d.ts.map +1 -1
- package/dist/types/internal.d.ts +2 -0
- package/dist/types/internal.d.ts.map +1 -1
- package/dist/types/types/transaction.d.ts +5 -0
- package/dist/types/types/transaction.d.ts.map +1 -1
- package/dist/types/util/AttestationEncoding.d.ts +118 -1
- package/dist/types/util/AttestationEncoding.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -274,6 +274,39 @@ attestations.forEach(att => {
|
|
|
274
274
|
});
|
|
275
275
|
```
|
|
276
276
|
|
|
277
|
+
#### Parsing Attestation Payloads
|
|
278
|
+
|
|
279
|
+
The SDK provides utilities to parse and verify signed attestation payloads:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
import { parseAttestationPayload } from "@trufnetwork/sdk-js";
|
|
283
|
+
import { sha256, recoverAddress } from "ethers";
|
|
284
|
+
|
|
285
|
+
// Get signed attestation
|
|
286
|
+
const signed = await attestationAction.getSignedAttestation({
|
|
287
|
+
requestTxId: result.requestTxId,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Extract canonical payload and signature
|
|
291
|
+
const canonicalPayload = signed.payload.slice(0, -65);
|
|
292
|
+
const signature = signed.payload.slice(-65);
|
|
293
|
+
|
|
294
|
+
// Verify signature
|
|
295
|
+
const digest = sha256(canonicalPayload);
|
|
296
|
+
const validatorAddress = recoverAddress(digest, {
|
|
297
|
+
r: "0x" + Buffer.from(signature.slice(0, 32)).toString("hex"),
|
|
298
|
+
s: "0x" + Buffer.from(signature.slice(32, 64)).toString("hex"),
|
|
299
|
+
v: signature[64]
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Parse and decode the payload
|
|
303
|
+
const parsed = parseAttestationPayload(canonicalPayload);
|
|
304
|
+
console.log(`Validator: ${validatorAddress}`);
|
|
305
|
+
console.log(`Query Results: ${parsed.result.length} rows`);
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**📖 For complete documentation including signature verification, payload structure, result decoding, and EVM integration examples, see the [Attestation Payload Parsing](./docs/api-reference.md#attestation-payload-parsing) section in the API Reference.**
|
|
309
|
+
|
|
277
310
|
#### Attestation Payload Structure
|
|
278
311
|
|
|
279
312
|
The signed attestation payload is a binary blob containing:
|
|
@@ -284,13 +317,36 @@ The signed attestation payload is a binary blob containing:
|
|
|
284
317
|
5. Stream ID (32 bytes, length-prefixed)
|
|
285
318
|
6. Action ID (2 bytes)
|
|
286
319
|
7. Arguments (variable, length-prefixed)
|
|
287
|
-
8. Result (variable, length-prefixed)
|
|
320
|
+
8. Result (variable, ABI-encoded, length-prefixed)
|
|
288
321
|
9. Signature (65 bytes, secp256k1)
|
|
289
322
|
|
|
290
323
|
This payload can be passed to EVM smart contracts for on-chain verification using `ecrecover`.
|
|
291
324
|
|
|
292
325
|
For a complete example, see [examples/attestation](./examples/attestation).
|
|
293
326
|
|
|
327
|
+
### Transaction Ledger Queries
|
|
328
|
+
|
|
329
|
+
Query transaction history, fees, and distributions for auditing and analytics.
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
// Get transaction details
|
|
333
|
+
const transactionAction = client.loadTransactionAction();
|
|
334
|
+
const txEvent = await transactionAction.getTransactionEvent({
|
|
335
|
+
txId: '0xabcdef...'
|
|
336
|
+
});
|
|
337
|
+
console.log(`Method: ${txEvent.method}, Fee: ${txEvent.feeAmount} wei`);
|
|
338
|
+
console.log(`Timestamp (ms): ${txEvent.stampMs}`);
|
|
339
|
+
|
|
340
|
+
// List fees paid by wallet
|
|
341
|
+
const entries = await transactionAction.listTransactionFees({
|
|
342
|
+
wallet: address,
|
|
343
|
+
mode: 'paid', // 'paid', 'received', or 'both'
|
|
344
|
+
limit: 10
|
|
345
|
+
});
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
**📖 For complete documentation including parameters, return types, pagination, filtering modes, and real-world examples, see the [Transaction Ledger Queries](./docs/api-reference.md#transaction-ledger-queries) section in the API Reference.**
|
|
349
|
+
|
|
294
350
|
### Transaction Lifecycle and Best Practices ⚠️
|
|
295
351
|
|
|
296
352
|
**Critical Understanding**: TN operations return success when transactions enter the mempool, NOT when they're executed on-chain. For operations where order matters, you must wait for transactions to be mined before proceeding.
|
|
@@ -428,7 +484,10 @@ For other bundlers or serverless platforms, consult their documentation on modul
|
|
|
428
484
|
| Get stream taxonomy | `composedAction.getTaxonomiesForStreams({streams, latestOnly})` |
|
|
429
485
|
| Request attestation | `attestationAction.requestAttestation({dataProvider, streamId, actionName, args, encryptSig, maxFee})` |
|
|
430
486
|
| Get signed attestation | `attestationAction.getSignedAttestation({requestTxId})` |
|
|
487
|
+
| Parse attestation payload | `parseAttestationPayload(canonicalPayload)` |
|
|
431
488
|
| List attestations | `attestationAction.listAttestations({requester, limit, offset, orderBy})` |
|
|
489
|
+
| Get transaction event | `transactionAction.getTransactionEvent({txId})` |
|
|
490
|
+
| List transaction fees | `transactionAction.listTransactionFees({wallet, mode, limit, offset})` |
|
|
432
491
|
| Destroy stream | `client.destroyStream(streamLocator)` |
|
|
433
492
|
|
|
434
493
|
**Safe Operation Pattern:**
|
|
@@ -23,6 +23,32 @@ __export(transactionAction_exports, {
|
|
|
23
23
|
TransactionAction: () => TransactionAction
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(transactionAction_exports);
|
|
26
|
+
var INDEXER_BASE = "https://indexer.infra.truf.network";
|
|
27
|
+
function normalizeTransactionId(txId) {
|
|
28
|
+
const lower = txId.toLowerCase();
|
|
29
|
+
return lower.startsWith("0x") ? lower : `0x${lower}`;
|
|
30
|
+
}
|
|
31
|
+
async function fetchTransactionStampMs(blockHeight, txId) {
|
|
32
|
+
const url = `${INDEXER_BASE}/v0/chain/transactions?from-block=${blockHeight}&to-block=${blockHeight}&order=desc`;
|
|
33
|
+
try {
|
|
34
|
+
const response = await fetch(url);
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
console.warn(`Indexer returned ${response.status} while fetching block ${blockHeight} for tx ${txId}`);
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
const data = await response.json();
|
|
40
|
+
if (!data.ok || !Array.isArray(data.data)) {
|
|
41
|
+
console.warn(`Indexer payload malformed for block ${blockHeight} (tx ${txId})`);
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
const normalizedTargetHash = normalizeTransactionId(txId);
|
|
45
|
+
const tx = data.data.find((entry) => normalizeTransactionId(entry.hash) === normalizedTargetHash);
|
|
46
|
+
return tx?.stamp_ms ?? 0;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.warn(`Failed to fetch stamp_ms for tx ${txId} at block ${blockHeight}`, error);
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
26
52
|
var TransactionAction = class _TransactionAction {
|
|
27
53
|
kwilClient;
|
|
28
54
|
kwilSigner;
|
|
@@ -114,9 +140,11 @@ var TransactionAction = class _TransactionAction {
|
|
|
114
140
|
if (!Number.isFinite(blockHeight) || blockHeight < 0) {
|
|
115
141
|
throw new Error(`Invalid block height: ${row.block_height} (tx: ${row.tx_id})`);
|
|
116
142
|
}
|
|
143
|
+
const stampMs = await fetchTransactionStampMs(blockHeight, row.tx_id);
|
|
117
144
|
return {
|
|
118
145
|
txId: row.tx_id,
|
|
119
146
|
blockHeight,
|
|
147
|
+
stampMs,
|
|
120
148
|
method: row.method,
|
|
121
149
|
caller: row.caller,
|
|
122
150
|
feeAmount,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/contracts-api/transactionAction.ts"],
|
|
4
|
-
"sourcesContent": ["import { KwilSigner, NodeKwil, WebKwil
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
4
|
+
"sourcesContent": ["import { KwilSigner, NodeKwil, WebKwil } from \"@trufnetwork/kwil-js\";\r\nimport { TransactionEvent, FeeDistribution, GetTransactionEventInput } from \"../types/transaction\";\r\n\r\nconst INDEXER_BASE = \"https://indexer.infra.truf.network\";\r\n\r\nfunction normalizeTransactionId(txId: string): string {\r\n const lower = txId.toLowerCase();\r\n return lower.startsWith(\"0x\") ? lower : `0x${lower}`;\r\n}\r\n\r\nasync function fetchTransactionStampMs(blockHeight: number, txId: string): Promise<number> {\r\n const url = `${INDEXER_BASE}/v0/chain/transactions?from-block=${blockHeight}&to-block=${blockHeight}&order=desc`;\r\n try {\r\n const response = await fetch(url);\r\n if (!response.ok) {\r\n console.warn(`Indexer returned ${response.status} while fetching block ${blockHeight} for tx ${txId}`);\r\n return 0;\r\n }\r\n\r\n const data = await response.json() as {\r\n ok: boolean;\r\n data: Array<{\r\n block_height: number;\r\n hash: string;\r\n stamp_ms: number | null;\r\n }>;\r\n };\r\n\r\n if (!data.ok || !Array.isArray(data.data)) {\r\n console.warn(`Indexer payload malformed for block ${blockHeight} (tx ${txId})`);\r\n return 0;\r\n }\r\n\r\n const normalizedTargetHash = normalizeTransactionId(txId);\r\n const tx = data.data.find(entry => normalizeTransactionId(entry.hash) === normalizedTargetHash);\r\n return tx?.stamp_ms ?? 0;\r\n } catch (error) {\r\n console.warn(`Failed to fetch stamp_ms for tx ${txId} at block ${blockHeight}`, error);\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Database row structure returned from get_transaction_event action\r\n */\r\ninterface TransactionEventRow {\r\n tx_id: string;\r\n block_height: string | number;\r\n method: string;\r\n caller: string;\r\n fee_amount: string | number;\r\n fee_recipient?: string | null;\r\n metadata?: string | null;\r\n fee_distributions: string;\r\n}\r\n\r\n/**\r\n * TransactionAction provides methods for querying transaction ledger data\r\n */\r\nexport class TransactionAction {\r\n protected kwilClient: WebKwil | NodeKwil;\r\n protected kwilSigner: KwilSigner;\r\n\r\n constructor(kwilClient: WebKwil | NodeKwil, kwilSigner: KwilSigner) {\r\n this.kwilClient = kwilClient;\r\n this.kwilSigner = kwilSigner;\r\n }\r\n\r\n /**\r\n * Fetches detailed transaction information by transaction hash\r\n *\r\n * @param input Transaction query input containing tx hash\r\n * @returns Promise resolving to transaction event with fee details\r\n * @throws Error if transaction not found or query fails\r\n *\r\n * @example\r\n * ```typescript\r\n * const txAction = client.loadTransactionAction();\r\n * const txEvent = await txAction.getTransactionEvent({\r\n * txId: \"0xabcdef123456...\"\r\n * });\r\n * console.log(`Method: ${txEvent.method}, Fee: ${txEvent.feeAmount} TRUF`);\r\n * ```\r\n */\r\n async getTransactionEvent(input: GetTransactionEventInput): Promise<TransactionEvent> {\r\n if (!input.txId || input.txId.trim() === \"\") {\r\n throw new Error(\"tx_id is required\");\r\n }\r\n\r\n const result = await this.kwilClient.call(\r\n {\r\n namespace: \"main\",\r\n name: \"get_transaction_event\",\r\n inputs: {\r\n $tx_id: input.txId,\r\n },\r\n },\r\n this.kwilSigner\r\n );\r\n\r\n if (result.status !== 200) {\r\n throw new Error(`Failed to get transaction event: HTTP ${result.status}`);\r\n }\r\n\r\n if (!result.data?.result || result.data.result.length === 0) {\r\n throw new Error(`Transaction not found: ${input.txId}`);\r\n }\r\n\r\n const row = result.data.result[0] as TransactionEventRow;\r\n\r\n // Validate required fields\r\n if (!row.method || typeof row.method !== 'string' || row.method.trim() === '') {\r\n throw new Error(`Missing or invalid method field (tx: ${row.tx_id})`);\r\n }\r\n\r\n if (!row.caller || typeof row.caller !== 'string' || row.caller.trim() === '') {\r\n throw new Error(`Missing or invalid caller field (tx: ${row.tx_id})`);\r\n }\r\n\r\n if (row.fee_amount === null || row.fee_amount === undefined) {\r\n throw new Error(`Missing fee_amount field (tx: ${row.tx_id})`);\r\n }\r\n\r\n // Validate fee_amount is numeric (can be string or number)\r\n const feeAmount = typeof row.fee_amount === 'string' ? row.fee_amount : String(row.fee_amount);\r\n const feeAmountNum = Number(feeAmount);\r\n if (isNaN(feeAmountNum) || !Number.isFinite(feeAmountNum)) {\r\n throw new Error(`Invalid fee_amount (not numeric): ${row.fee_amount} (tx: ${row.tx_id})`);\r\n }\r\n if (feeAmountNum < 0) {\r\n throw new Error(`Invalid fee_amount (negative): ${row.fee_amount} (tx: ${row.tx_id})`);\r\n }\r\n\r\n // Parse fee_distributions string: \"recipient1:amount1,recipient2:amount2\"\r\n const feeDistributions: FeeDistribution[] = [];\r\n if (row.fee_distributions && row.fee_distributions !== \"\") {\r\n const parts = row.fee_distributions.split(\",\");\r\n for (const part of parts) {\r\n const trimmedPart = part.trim();\r\n if (trimmedPart) {\r\n // Split only on first colon to handle addresses with colons\r\n const colonIndex = trimmedPart.indexOf(\":\");\r\n if (colonIndex === -1) {\r\n throw new Error(`Invalid fee distribution format (missing colon): ${trimmedPart} (tx: ${row.tx_id})`);\r\n }\r\n\r\n const recipient = trimmedPart.substring(0, colonIndex).trim();\r\n const amount = trimmedPart.substring(colonIndex + 1).trim();\r\n\r\n if (!recipient || !amount) {\r\n throw new Error(`Invalid fee distribution entry (empty recipient or amount): ${trimmedPart} (tx: ${row.tx_id})`);\r\n }\r\n\r\n // Validate amount is numeric and non-negative\r\n const amt = Number(amount);\r\n if (isNaN(amt) || !Number.isFinite(amt)) {\r\n throw new Error(`Invalid fee distribution amount (not numeric): ${amount} (tx: ${row.tx_id})`);\r\n }\r\n if (amt < 0) {\r\n throw new Error(`Invalid fee distribution amount (negative): ${amount} (tx: ${row.tx_id})`);\r\n }\r\n\r\n feeDistributions.push({ recipient, amount });\r\n }\r\n }\r\n }\r\n\r\n // Validate block height\r\n const blockHeight = typeof row.block_height === 'number'\r\n ? row.block_height\r\n : parseInt(row.block_height, 10);\r\n if (!Number.isFinite(blockHeight) || blockHeight < 0) {\r\n throw new Error(`Invalid block height: ${row.block_height} (tx: ${row.tx_id})`);\r\n }\r\n\r\n const stampMs = await fetchTransactionStampMs(blockHeight, row.tx_id);\r\n\r\n return {\r\n txId: row.tx_id,\r\n blockHeight,\r\n stampMs,\r\n method: row.method,\r\n caller: row.caller,\r\n feeAmount,\r\n feeRecipient: row.fee_recipient || undefined,\r\n metadata: row.metadata || undefined,\r\n feeDistributions,\r\n };\r\n }\r\n\r\n /**\r\n * Creates a TransactionAction instance from an existing client and signer\r\n *\r\n * @param kwilClient The Kwil client (Web or Node)\r\n * @param kwilSigner The Kwil signer for authentication\r\n * @returns A new TransactionAction instance\r\n */\r\n static fromClient(kwilClient: WebKwil | NodeKwil, kwilSigner: KwilSigner): TransactionAction {\r\n return new TransactionAction(kwilClient, kwilSigner);\r\n }\r\n}\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/cjs/internal.cjs
CHANGED
|
@@ -31,6 +31,7 @@ __export(internal_exports, {
|
|
|
31
31
|
StreamType: () => import_contractValues.StreamType,
|
|
32
32
|
deleteStream: () => import_deleteStream.deleteStream,
|
|
33
33
|
deployStream: () => import_deployStream.deployStream,
|
|
34
|
+
parseAttestationPayload: () => import_AttestationEncoding.parseAttestationPayload,
|
|
34
35
|
visibility: () => import_visibility.visibility
|
|
35
36
|
});
|
|
36
37
|
module.exports = __toCommonJS(internal_exports);
|
|
@@ -45,5 +46,6 @@ var import_deleteStream = require("./contracts-api/deleteStream.cjs");
|
|
|
45
46
|
var import_StreamId = require("./util/StreamId.cjs");
|
|
46
47
|
var import_EthereumAddress = require("./util/EthereumAddress.cjs");
|
|
47
48
|
var import_visibility = require("./util/visibility.cjs");
|
|
49
|
+
var import_AttestationEncoding = require("./util/AttestationEncoding.cjs");
|
|
48
50
|
var import_contractValues = require("./contracts-api/contractValues.cjs");
|
|
49
51
|
//# sourceMappingURL=internal.cjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/internal.ts"],
|
|
4
|
-
"sourcesContent": ["/**\r\n * Internal module to manage exports and break circular dependencies\r\n * This centralizes all exports to prevent circular import issues\r\n */\r\n\r\n// Base client and types\r\nexport { BaseTNClient } from \"./client/client\";\r\nexport type { TNClientOptions, SignerInfo, ListStreamsInput, GetLastTransactionsInput } from \"./client/client\";\r\n\r\n// Contract APIs\r\nexport { Action } from \"./contracts-api/action\";\r\nexport { PrimitiveAction } from \"./contracts-api/primitiveAction\";\r\nexport { ComposedAction } from \"./contracts-api/composedAction\";\r\nexport { RoleManagement } from \"./contracts-api/roleManagement\";\r\nexport { AttestationAction } from \"./contracts-api/attestationAction\";\r\nexport { deployStream } from \"./contracts-api/deployStream\";\r\nexport { deleteStream } from \"./contracts-api/deleteStream\";\r\n\r\n// Utility classes\r\nexport { StreamId } from \"./util/StreamId\";\r\nexport { EthereumAddress } from \"./util/EthereumAddress\";\r\nexport { visibility } from \"./util/visibility\";\r\n\r\n// Contract values and types\r\nexport { StreamType } from \"./contracts-api/contractValues\";\r\n\r\n// Stream types\r\nexport type { StreamLocator } from \"./types/stream\";\r\n\r\n// Action types\r\nexport type {\r\n StreamRecord,\r\n ListMetadataByHeightParams,\r\n MetadataQueryResult,\r\n GetRecordInput,\r\n GetFirstRecordInput\r\n} from \"./contracts-api/action\";\r\n\r\n// Primitive action types\r\nexport type { InsertRecordInput } from \"./contracts-api/primitiveAction\";\r\n\r\n// Composed action types\r\nexport type {\r\n TaxonomySet,\r\n TaxonomyItem,\r\n ListTaxonomiesByHeightParams,\r\n GetTaxonomiesForStreamsParams,\r\n TaxonomyQueryResult\r\n} from \"./contracts-api/composedAction\";\r\n\r\n// Role management types\r\nexport type {\r\n GrantRoleInput,\r\n RevokeRoleInput,\r\n AreMembersOfInput,\r\n WalletMembership\r\n} from \"./types/role\";\r\n\r\n// Attestation types\r\nexport type {\r\n RequestAttestationInput,\r\n RequestAttestationResult,\r\n GetSignedAttestationInput,\r\n SignedAttestationResult,\r\n ListAttestationsInput,\r\n AttestationMetadata\r\n} from \"./types/attestation\";\r\n\r\n// Visibility types\r\nexport type { VisibilityEnum } from \"./util/visibility\";"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,oBAA6B;AAI7B,oBAAuB;AACvB,6BAAgC;AAChC,4BAA+B;AAC/B,4BAA+B;AAC/B,+BAAkC;AAClC,0BAA6B;AAC7B,0BAA6B;AAG7B,sBAAyB;AACzB,6BAAgC;AAChC,wBAA2B;AAG3B,4BAA2B;",
|
|
4
|
+
"sourcesContent": ["/**\r\n * Internal module to manage exports and break circular dependencies\r\n * This centralizes all exports to prevent circular import issues\r\n */\r\n\r\n// Base client and types\r\nexport { BaseTNClient } from \"./client/client\";\r\nexport type { TNClientOptions, SignerInfo, ListStreamsInput, GetLastTransactionsInput } from \"./client/client\";\r\n\r\n// Contract APIs\r\nexport { Action } from \"./contracts-api/action\";\r\nexport { PrimitiveAction } from \"./contracts-api/primitiveAction\";\r\nexport { ComposedAction } from \"./contracts-api/composedAction\";\r\nexport { RoleManagement } from \"./contracts-api/roleManagement\";\r\nexport { AttestationAction } from \"./contracts-api/attestationAction\";\r\nexport { deployStream } from \"./contracts-api/deployStream\";\r\nexport { deleteStream } from \"./contracts-api/deleteStream\";\r\n\r\n// Utility classes\r\nexport { StreamId } from \"./util/StreamId\";\r\nexport { EthereumAddress } from \"./util/EthereumAddress\";\r\nexport { visibility } from \"./util/visibility\";\r\n\r\n// Attestation encoding/decoding utilities\r\nexport {\r\n parseAttestationPayload\r\n} from \"./util/AttestationEncoding\";\r\n\r\nexport type {\r\n DecodedRow,\r\n ParsedAttestationPayload\r\n} from \"./util/AttestationEncoding\";\r\n\r\n// Contract values and types\r\nexport { StreamType } from \"./contracts-api/contractValues\";\r\n\r\n// Stream types\r\nexport type { StreamLocator } from \"./types/stream\";\r\n\r\n// Action types\r\nexport type {\r\n StreamRecord,\r\n ListMetadataByHeightParams,\r\n MetadataQueryResult,\r\n GetRecordInput,\r\n GetFirstRecordInput\r\n} from \"./contracts-api/action\";\r\n\r\n// Primitive action types\r\nexport type { InsertRecordInput } from \"./contracts-api/primitiveAction\";\r\n\r\n// Composed action types\r\nexport type {\r\n TaxonomySet,\r\n TaxonomyItem,\r\n ListTaxonomiesByHeightParams,\r\n GetTaxonomiesForStreamsParams,\r\n TaxonomyQueryResult\r\n} from \"./contracts-api/composedAction\";\r\n\r\n// Role management types\r\nexport type {\r\n GrantRoleInput,\r\n RevokeRoleInput,\r\n AreMembersOfInput,\r\n WalletMembership\r\n} from \"./types/role\";\r\n\r\n// Attestation types\r\nexport type {\r\n RequestAttestationInput,\r\n RequestAttestationResult,\r\n GetSignedAttestationInput,\r\n SignedAttestationResult,\r\n ListAttestationsInput,\r\n AttestationMetadata\r\n} from \"./types/attestation\";\r\n\r\n// Visibility types\r\nexport type { VisibilityEnum } from \"./util/visibility\";"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,oBAA6B;AAI7B,oBAAuB;AACvB,6BAAgC;AAChC,4BAA+B;AAC/B,4BAA+B;AAC/B,+BAAkC;AAClC,0BAA6B;AAC7B,0BAA6B;AAG7B,sBAAyB;AACzB,6BAAgC;AAChC,wBAA2B;AAG3B,iCAEO;AAQP,4BAA2B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/types/transaction.ts"],
|
|
4
|
-
"sourcesContent": ["export interface LastTransaction {\r\n /** Block height */\r\n blockHeight: number;\r\n /** Which action was taken */\r\n method: string;\r\n /** Address that sent the on\u2010chain tx */\r\n sender: string;\r\n /** Hash of the on\u2010chain transaction */\r\n transactionHash: string;\r\n /** Millisecond timestamp from the block header */\r\n stampMs: number;\r\n}\r\n\r\n/**\r\n * Represents a single fee distribution\r\n */\r\nexport interface FeeDistribution {\r\n /** Recipient Ethereum address */\r\n recipient: string;\r\n /** Amount as string (handles large numbers) */\r\n amount: string;\r\n}\r\n\r\n/**\r\n * Represents a single transaction event from the ledger\r\n *\r\n * Fee Fields Relationship:\r\n * - feeAmount: Total fee charged for the transaction (as string to handle large numbers)\r\n * - feeRecipient: Primary fee recipient address (if single recipient). May be undefined for:\r\n * 1. Transactions from fee-exempt wallets (system:network_writer role)\r\n * 2. Transactions with multiple fee distributions (use feeDistributions instead)\r\n * - feeDistributions: Array of fee distributions to multiple recipients\r\n * - Aggregated using string_agg() in get_transaction_event query\r\n * - Parsed from \"recipient1:amount1,recipient2:amount2\" format\r\n * - Sum of amounts in feeDistributions equals feeAmount (when present)\r\n * - Empty array when there are no distributions or single recipient (use feeRecipient)\r\n */\r\nexport interface TransactionEvent {\r\n /** Transaction hash (0x-prefixed) */\r\n txId: string;\r\n /** Block height when transaction was included */\r\n blockHeight: number;\r\n /** Method name (e.g., \"deployStream\", \"insertRecords\") */\r\n method: string;\r\n /** Ethereum address of caller (lowercase, 0x-prefixed) */\r\n caller: string;\r\n /**\r\n * Total fee amount as string (handles large numbers with 18 decimals)\r\n * Will be \"0\" for fee-exempt wallets (system:network_writer role)\r\n */\r\n feeAmount: string;\r\n /**\r\n * Primary fee recipient address (lowercase, 0x-prefixed)\r\n * Undefined when:\r\n * - Wallet is fee-exempt (feeAmount === \"0\")\r\n * - Fee has multiple distributions (check feeDistributions)\r\n */\r\n feeRecipient?: string;\r\n /** Optional metadata JSON (nullable) */\r\n metadata?: string;\r\n /**\r\n * Array of fee distributions to multiple recipients\r\n * Aggregated from transaction_event_distributions table using string_agg()\r\n */\r\n feeDistributions: FeeDistribution[];\r\n}\r\n\r\n/**\r\n * Input for getting transaction event\r\n */\r\nexport interface GetTransactionEventInput {\r\n /** Transaction hash (with or without 0x prefix) */\r\n txId: string;\r\n}\r\n"],
|
|
4
|
+
"sourcesContent": ["export interface LastTransaction {\r\n /** Block height */\r\n blockHeight: number;\r\n /** Which action was taken */\r\n method: string;\r\n /** Address that sent the on\u2010chain tx */\r\n sender: string;\r\n /** Hash of the on\u2010chain transaction */\r\n transactionHash: string;\r\n /** Millisecond timestamp from the block header */\r\n stampMs: number;\r\n}\r\n\r\n/**\r\n * Represents a single fee distribution\r\n */\r\nexport interface FeeDistribution {\r\n /** Recipient Ethereum address */\r\n recipient: string;\r\n /** Amount as string (handles large numbers) */\r\n amount: string;\r\n}\r\n\r\n/**\r\n * Represents a single transaction event from the ledger\r\n *\r\n * Fee Fields Relationship:\r\n * - feeAmount: Total fee charged for the transaction (as string to handle large numbers)\r\n * - feeRecipient: Primary fee recipient address (if single recipient). May be undefined for:\r\n * 1. Transactions from fee-exempt wallets (system:network_writer role)\r\n * 2. Transactions with multiple fee distributions (use feeDistributions instead)\r\n * - feeDistributions: Array of fee distributions to multiple recipients\r\n * - Aggregated using string_agg() in get_transaction_event query\r\n * - Parsed from \"recipient1:amount1,recipient2:amount2\" format\r\n * - Sum of amounts in feeDistributions equals feeAmount (when present)\r\n * - Empty array when there are no distributions or single recipient (use feeRecipient)\r\n */\r\nexport interface TransactionEvent {\r\n /** Transaction hash (0x-prefixed) */\r\n txId: string;\r\n /** Block height when transaction was included */\r\n blockHeight: number;\r\n /**\r\n * Millisecond timestamp from the block header via indexer lookup.\r\n * Will be 0 when the indexer is unavailable.\r\n */\r\n stampMs: number;\r\n /** Method name (e.g., \"deployStream\", \"insertRecords\") */\r\n method: string;\r\n /** Ethereum address of caller (lowercase, 0x-prefixed) */\r\n caller: string;\r\n /**\r\n * Total fee amount as string (handles large numbers with 18 decimals)\r\n * Will be \"0\" for fee-exempt wallets (system:network_writer role)\r\n */\r\n feeAmount: string;\r\n /**\r\n * Primary fee recipient address (lowercase, 0x-prefixed)\r\n * Undefined when:\r\n * - Wallet is fee-exempt (feeAmount === \"0\")\r\n * - Fee has multiple distributions (check feeDistributions)\r\n */\r\n feeRecipient?: string;\r\n /** Optional metadata JSON (nullable) */\r\n metadata?: string;\r\n /**\r\n * Array of fee distributions to multiple recipients\r\n * Aggregated from transaction_event_distributions table using string_agg()\r\n */\r\n feeDistributions: FeeDistribution[];\r\n}\r\n\r\n/**\r\n * Input for getting transaction event\r\n */\r\nexport interface GetTransactionEventInput {\r\n /** Transaction hash (with or without 0x prefix) */\r\n txId: string;\r\n}\r\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;AAAA;AAAA;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -20,11 +20,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/util/AttestationEncoding.ts
|
|
21
21
|
var AttestationEncoding_exports = {};
|
|
22
22
|
__export(AttestationEncoding_exports, {
|
|
23
|
+
decodeABIDatapoints: () => decodeABIDatapoints,
|
|
24
|
+
decodeCanonicalQueryResult: () => decodeCanonicalQueryResult,
|
|
25
|
+
decodeEncodedValue: () => decodeEncodedValue,
|
|
26
|
+
decodedValueToJS: () => decodedValueToJS,
|
|
23
27
|
encodeActionArgs: () => encodeActionArgs,
|
|
28
|
+
parseAttestationPayload: () => parseAttestationPayload,
|
|
29
|
+
readUint16BE: () => readUint16BE,
|
|
30
|
+
readUint16LE: () => readUint16LE,
|
|
31
|
+
readUint32BE: () => readUint32BE,
|
|
24
32
|
readUint32LE: () => readUint32LE
|
|
25
33
|
});
|
|
26
34
|
module.exports = __toCommonJS(AttestationEncoding_exports);
|
|
27
35
|
var import_kwil_js = require("@trufnetwork/kwil-js");
|
|
36
|
+
var import_ethers = require("ethers");
|
|
28
37
|
var import_meta = {};
|
|
29
38
|
function encodeActionArgs(args) {
|
|
30
39
|
const encodedArgs = [];
|
|
@@ -61,6 +70,303 @@ function writeUint32LE(buffer, value, offset) {
|
|
|
61
70
|
function readUint32LE(buffer, offset) {
|
|
62
71
|
return (buffer[offset] | buffer[offset + 1] << 8 | buffer[offset + 2] << 16 | buffer[offset + 3] << 24) >>> 0;
|
|
63
72
|
}
|
|
73
|
+
function readUint16LE(buffer, offset) {
|
|
74
|
+
return (buffer[offset] | buffer[offset + 1] << 8) >>> 0;
|
|
75
|
+
}
|
|
76
|
+
function readUint32BE(buffer, offset) {
|
|
77
|
+
return (buffer[offset] << 24 | buffer[offset + 1] << 16 | buffer[offset + 2] << 8 | buffer[offset + 3]) >>> 0;
|
|
78
|
+
}
|
|
79
|
+
function readUint16BE(buffer, offset) {
|
|
80
|
+
return (buffer[offset] << 8 | buffer[offset + 1]) >>> 0;
|
|
81
|
+
}
|
|
82
|
+
function decodeDataType(buffer, offset) {
|
|
83
|
+
const version = readUint16BE(buffer, offset);
|
|
84
|
+
offset += 2;
|
|
85
|
+
if (version !== 0) {
|
|
86
|
+
throw new Error(`Unsupported DataType version: ${version}`);
|
|
87
|
+
}
|
|
88
|
+
const nameLen = readUint32BE(buffer, offset);
|
|
89
|
+
offset += 4;
|
|
90
|
+
const nameBytes = buffer.slice(offset, offset + nameLen);
|
|
91
|
+
const name = new TextDecoder().decode(nameBytes);
|
|
92
|
+
offset += nameLen;
|
|
93
|
+
const is_array = buffer[offset] === 1;
|
|
94
|
+
offset += 1;
|
|
95
|
+
const metadata0 = readUint16BE(buffer, offset);
|
|
96
|
+
offset += 2;
|
|
97
|
+
const metadata1 = readUint16BE(buffer, offset);
|
|
98
|
+
offset += 2;
|
|
99
|
+
return {
|
|
100
|
+
type: {
|
|
101
|
+
name,
|
|
102
|
+
is_array,
|
|
103
|
+
metadata: [metadata0, metadata1]
|
|
104
|
+
},
|
|
105
|
+
offset
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function decodeEncodedValue(buffer, offset = 0) {
|
|
109
|
+
const version = readUint16LE(buffer, offset);
|
|
110
|
+
offset += 2;
|
|
111
|
+
if (version !== 0) {
|
|
112
|
+
throw new Error(`Unsupported EncodedValue version: ${version}`);
|
|
113
|
+
}
|
|
114
|
+
const typeLen = readUint32LE(buffer, offset);
|
|
115
|
+
offset += 4;
|
|
116
|
+
const typeBytes = buffer.slice(offset, offset + typeLen);
|
|
117
|
+
const { type } = decodeDataType(typeBytes, 0);
|
|
118
|
+
offset += typeLen;
|
|
119
|
+
const dataLen = readUint16LE(buffer, offset);
|
|
120
|
+
offset += 2;
|
|
121
|
+
const data = [];
|
|
122
|
+
for (let i = 0; i < dataLen; i++) {
|
|
123
|
+
const itemLen = readUint32LE(buffer, offset);
|
|
124
|
+
offset += 4;
|
|
125
|
+
const itemBytes = buffer.slice(offset, offset + itemLen);
|
|
126
|
+
data.push(itemBytes);
|
|
127
|
+
offset += itemLen;
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
value: { type, data },
|
|
131
|
+
offset
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function decodedValueToJS(decoded) {
|
|
135
|
+
if (decoded.data.length === 0) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const firstItem = decoded.data[0];
|
|
139
|
+
if (firstItem.length === 0 || firstItem[0] === 0) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
const valueBytes = firstItem.slice(1);
|
|
143
|
+
const typeName = decoded.type.name.toLowerCase();
|
|
144
|
+
if (decoded.type.is_array) {
|
|
145
|
+
const result = [];
|
|
146
|
+
for (const item of decoded.data) {
|
|
147
|
+
if (item.length === 0 || item[0] === 0) {
|
|
148
|
+
result.push(null);
|
|
149
|
+
} else {
|
|
150
|
+
const itemBytes = item.slice(1);
|
|
151
|
+
result.push(decodeSingleValue(typeName, itemBytes));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
return decodeSingleValue(typeName, valueBytes);
|
|
157
|
+
}
|
|
158
|
+
function decodeSingleValue(typeName, bytes) {
|
|
159
|
+
switch (typeName) {
|
|
160
|
+
case "text":
|
|
161
|
+
case "uuid":
|
|
162
|
+
return new TextDecoder().decode(bytes);
|
|
163
|
+
case "int":
|
|
164
|
+
case "int8":
|
|
165
|
+
case "integer":
|
|
166
|
+
if (bytes.length === 8) {
|
|
167
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
168
|
+
return view.getBigInt64(0, false);
|
|
169
|
+
}
|
|
170
|
+
throw new Error(`Invalid integer byte length: expected 8, got ${bytes.length}`);
|
|
171
|
+
case "bool":
|
|
172
|
+
case "boolean":
|
|
173
|
+
return bytes.length > 0 && bytes[0] === 1;
|
|
174
|
+
case "numeric":
|
|
175
|
+
case "decimal":
|
|
176
|
+
return new TextDecoder().decode(bytes);
|
|
177
|
+
case "bytea":
|
|
178
|
+
case "blob":
|
|
179
|
+
return bytes;
|
|
180
|
+
default:
|
|
181
|
+
try {
|
|
182
|
+
return new TextDecoder().decode(bytes);
|
|
183
|
+
} catch {
|
|
184
|
+
return bytes;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function decodeCanonicalQueryResult(data) {
|
|
189
|
+
let offset = 0;
|
|
190
|
+
if (data.length < 4) {
|
|
191
|
+
throw new Error("Data too short for row count");
|
|
192
|
+
}
|
|
193
|
+
const rowCount = readUint32LE(data, offset);
|
|
194
|
+
offset += 4;
|
|
195
|
+
const rows = [];
|
|
196
|
+
for (let i = 0; i < rowCount; i++) {
|
|
197
|
+
if (offset + 4 > data.length) {
|
|
198
|
+
throw new Error(`Data too short for column count at row ${i}`);
|
|
199
|
+
}
|
|
200
|
+
const colCount = readUint32LE(data, offset);
|
|
201
|
+
offset += 4;
|
|
202
|
+
const values = [];
|
|
203
|
+
for (let j = 0; j < colCount; j++) {
|
|
204
|
+
if (offset + 4 > data.length) {
|
|
205
|
+
throw new Error(`Data too short for column ${j} length at row ${i}`);
|
|
206
|
+
}
|
|
207
|
+
const colLen = readUint32LE(data, offset);
|
|
208
|
+
offset += 4;
|
|
209
|
+
if (offset + colLen > data.length) {
|
|
210
|
+
throw new Error(`Data too short for column ${j} bytes at row ${i}`);
|
|
211
|
+
}
|
|
212
|
+
const colBytes = data.slice(offset, offset + colLen);
|
|
213
|
+
const { value: decodedValue } = decodeEncodedValue(colBytes, 0);
|
|
214
|
+
const jsValue = decodedValueToJS(decodedValue);
|
|
215
|
+
values.push(jsValue);
|
|
216
|
+
offset += colLen;
|
|
217
|
+
}
|
|
218
|
+
rows.push({ values });
|
|
219
|
+
}
|
|
220
|
+
return rows;
|
|
221
|
+
}
|
|
222
|
+
function decodeABIDatapoints(data) {
|
|
223
|
+
if (!data || data.length === 0) {
|
|
224
|
+
return [];
|
|
225
|
+
}
|
|
226
|
+
const abiCoder = import_ethers.AbiCoder.defaultAbiCoder();
|
|
227
|
+
try {
|
|
228
|
+
const decoded = abiCoder.decode(
|
|
229
|
+
["uint256[]", "int256[]"],
|
|
230
|
+
data
|
|
231
|
+
);
|
|
232
|
+
const timestamps = decoded[0];
|
|
233
|
+
const values = decoded[1];
|
|
234
|
+
if (timestamps.length !== values.length) {
|
|
235
|
+
throw new Error(`Timestamp/value array length mismatch: ${timestamps.length} vs ${values.length}`);
|
|
236
|
+
}
|
|
237
|
+
const rows = [];
|
|
238
|
+
for (let i = 0; i < timestamps.length; i++) {
|
|
239
|
+
rows.push({
|
|
240
|
+
values: [
|
|
241
|
+
timestamps[i].toString(),
|
|
242
|
+
// Convert from 18-decimal fixed point to decimal string
|
|
243
|
+
formatFixedPoint(values[i], 18)
|
|
244
|
+
]
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return rows;
|
|
248
|
+
} catch (err) {
|
|
249
|
+
throw new Error(`Failed to decode ABI datapoints: ${err}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function formatFixedPoint(value, decimals) {
|
|
253
|
+
const isNegative = value < 0n;
|
|
254
|
+
const absValue = isNegative ? -value : value;
|
|
255
|
+
const divisor = 10n ** BigInt(decimals);
|
|
256
|
+
const integerPart = absValue / divisor;
|
|
257
|
+
const fractionalPart = absValue % divisor;
|
|
258
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
259
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
260
|
+
if (trimmedFractional === "") {
|
|
261
|
+
return `${isNegative ? "-" : ""}${integerPart}`;
|
|
262
|
+
}
|
|
263
|
+
return `${isNegative ? "-" : ""}${integerPart}.${trimmedFractional}`;
|
|
264
|
+
}
|
|
265
|
+
function parseAttestationPayload(payload) {
|
|
266
|
+
let offset = 0;
|
|
267
|
+
if (payload.length < 1) {
|
|
268
|
+
throw new Error("Payload too short for version");
|
|
269
|
+
}
|
|
270
|
+
const version = payload[offset];
|
|
271
|
+
offset += 1;
|
|
272
|
+
if (offset >= payload.length) {
|
|
273
|
+
throw new Error("Payload too short for algorithm");
|
|
274
|
+
}
|
|
275
|
+
const algorithm = payload[offset];
|
|
276
|
+
offset += 1;
|
|
277
|
+
if (offset + 8 > payload.length) {
|
|
278
|
+
throw new Error("Payload too short for block height");
|
|
279
|
+
}
|
|
280
|
+
const blockHeightHigh = readUint32BE(payload, offset);
|
|
281
|
+
const blockHeightLow = readUint32BE(payload, offset + 4);
|
|
282
|
+
const blockHeight = BigInt(blockHeightHigh) << 32n | BigInt(blockHeightLow);
|
|
283
|
+
offset += 8;
|
|
284
|
+
if (offset + 4 > payload.length) {
|
|
285
|
+
throw new Error("Payload too short for data provider length");
|
|
286
|
+
}
|
|
287
|
+
const dataProviderLen = readUint32BE(payload, offset);
|
|
288
|
+
offset += 4;
|
|
289
|
+
if (offset + dataProviderLen > payload.length) {
|
|
290
|
+
throw new Error("Payload too short for data provider");
|
|
291
|
+
}
|
|
292
|
+
const dataProviderBytes = payload.slice(offset, offset + dataProviderLen);
|
|
293
|
+
let dataProvider;
|
|
294
|
+
if (dataProviderLen === 20) {
|
|
295
|
+
dataProvider = "0x" + Array.from(dataProviderBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
296
|
+
} else {
|
|
297
|
+
try {
|
|
298
|
+
const decoded = new TextDecoder().decode(dataProviderBytes);
|
|
299
|
+
if (decoded.startsWith("0x") && /^0x[0-9a-fA-F]+$/.test(decoded)) {
|
|
300
|
+
dataProvider = decoded;
|
|
301
|
+
} else {
|
|
302
|
+
dataProvider = decoded;
|
|
303
|
+
}
|
|
304
|
+
} catch {
|
|
305
|
+
dataProvider = "0x" + Array.from(dataProviderBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
offset += dataProviderLen;
|
|
309
|
+
if (offset + 4 > payload.length) {
|
|
310
|
+
throw new Error("Payload too short for stream ID length");
|
|
311
|
+
}
|
|
312
|
+
const streamIdLen = readUint32BE(payload, offset);
|
|
313
|
+
offset += 4;
|
|
314
|
+
if (offset + streamIdLen > payload.length) {
|
|
315
|
+
throw new Error("Payload too short for stream ID");
|
|
316
|
+
}
|
|
317
|
+
const streamIdBytes = payload.slice(offset, offset + streamIdLen);
|
|
318
|
+
const streamId = new TextDecoder().decode(streamIdBytes);
|
|
319
|
+
offset += streamIdLen;
|
|
320
|
+
if (offset + 2 > payload.length) {
|
|
321
|
+
throw new Error("Payload too short for action ID");
|
|
322
|
+
}
|
|
323
|
+
const actionId = readUint16BE(payload, offset);
|
|
324
|
+
offset += 2;
|
|
325
|
+
if (offset + 4 > payload.length) {
|
|
326
|
+
throw new Error("Payload too short for arguments length");
|
|
327
|
+
}
|
|
328
|
+
const argsLen = readUint32BE(payload, offset);
|
|
329
|
+
offset += 4;
|
|
330
|
+
if (offset + argsLen > payload.length) {
|
|
331
|
+
throw new Error("Payload too short for arguments");
|
|
332
|
+
}
|
|
333
|
+
const argsBytes = payload.slice(offset, offset + argsLen);
|
|
334
|
+
offset += argsLen;
|
|
335
|
+
let args = [];
|
|
336
|
+
if (argsLen > 0) {
|
|
337
|
+
let argsOffset = 0;
|
|
338
|
+
const argCount = readUint32LE(argsBytes, argsOffset);
|
|
339
|
+
argsOffset += 4;
|
|
340
|
+
for (let i = 0; i < argCount; i++) {
|
|
341
|
+
const argLen = readUint32LE(argsBytes, argsOffset);
|
|
342
|
+
argsOffset += 4;
|
|
343
|
+
const argBytes = argsBytes.slice(argsOffset, argsOffset + argLen);
|
|
344
|
+
const { value: decodedArg } = decodeEncodedValue(argBytes, 0);
|
|
345
|
+
args.push(decodedValueToJS(decodedArg));
|
|
346
|
+
argsOffset += argLen;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (offset + 4 > payload.length) {
|
|
350
|
+
throw new Error("Payload too short for result length");
|
|
351
|
+
}
|
|
352
|
+
const resultLen = readUint32BE(payload, offset);
|
|
353
|
+
offset += 4;
|
|
354
|
+
if (offset + resultLen > payload.length) {
|
|
355
|
+
throw new Error("Payload too short for result");
|
|
356
|
+
}
|
|
357
|
+
const resultBytes = payload.slice(offset, offset + resultLen);
|
|
358
|
+
const result = decodeABIDatapoints(resultBytes);
|
|
359
|
+
return {
|
|
360
|
+
version,
|
|
361
|
+
algorithm,
|
|
362
|
+
blockHeight,
|
|
363
|
+
dataProvider,
|
|
364
|
+
streamId,
|
|
365
|
+
actionId,
|
|
366
|
+
arguments: args,
|
|
367
|
+
result
|
|
368
|
+
};
|
|
369
|
+
}
|
|
64
370
|
if (import_meta.vitest) {
|
|
65
371
|
const { describe, it, expect } = import_meta.vitest;
|
|
66
372
|
describe("encodeActionArgs", () => {
|
|
@@ -153,5 +459,40 @@ if (import_meta.vitest) {
|
|
|
153
459
|
expect(buffer[3]).toBe(18);
|
|
154
460
|
});
|
|
155
461
|
});
|
|
462
|
+
describe("readUint16LE and readUint16BE", () => {
|
|
463
|
+
it("should read uint16 little-endian correctly", () => {
|
|
464
|
+
const buffer = new Uint8Array([120, 86]);
|
|
465
|
+
expect(readUint16LE(buffer, 0)).toBe(22136);
|
|
466
|
+
});
|
|
467
|
+
it("should read uint16 big-endian correctly", () => {
|
|
468
|
+
const buffer = new Uint8Array([86, 120]);
|
|
469
|
+
expect(readUint16BE(buffer, 0)).toBe(22136);
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
describe("readUint32BE", () => {
|
|
473
|
+
it("should read uint32 big-endian correctly", () => {
|
|
474
|
+
const buffer = new Uint8Array([18, 52, 86, 120]);
|
|
475
|
+
expect(readUint32BE(buffer, 0)).toBe(305419896);
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
describe("decodeCanonicalQueryResult", () => {
|
|
479
|
+
it("should decode empty result (0 rows)", () => {
|
|
480
|
+
const buffer = new Uint8Array(4);
|
|
481
|
+
writeUint32LE(buffer, 0, 0);
|
|
482
|
+
const result = decodeCanonicalQueryResult(buffer);
|
|
483
|
+
expect(result.length).toBe(0);
|
|
484
|
+
});
|
|
485
|
+
it("should throw on invalid data", () => {
|
|
486
|
+
const buffer = new Uint8Array(2);
|
|
487
|
+
expect(() => decodeCanonicalQueryResult(buffer)).toThrow("Data too short for row count");
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
describe("parseAttestationPayload", () => {
|
|
491
|
+
it.todo("should parse payload with ABI-encoded result (TODO: need to construct synthetic test data with valid ABI encoding - see examples/attestation/index.ts for working integration test)");
|
|
492
|
+
it("should throw on invalid version", () => {
|
|
493
|
+
const payload = new Uint8Array(1);
|
|
494
|
+
expect(() => parseAttestationPayload(payload)).toThrow();
|
|
495
|
+
});
|
|
496
|
+
});
|
|
156
497
|
}
|
|
157
498
|
//# sourceMappingURL=AttestationEncoding.cjs.map
|