Skip to content

Commit cca416d

Browse files
authored
feat: view latest transactions (#133)
1 parent 93e7768 commit cca416d

File tree

3 files changed

+113
-51
lines changed

3 files changed

+113
-51
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { NodeTNClient } from "../../src/index";
2+
import { Wallet } from "ethers";
3+
4+
const wallet = new Wallet("0000000000000000000000000000000000000000000000000000000000000001");
5+
6+
const client = new NodeTNClient({
7+
endpoint: "https://gateway.mainnet.truf.network",
8+
signerInfo: {
9+
address: wallet.address,
10+
signer: wallet,
11+
},
12+
chainId: "tn-v2.1",
13+
timeout: 30000,
14+
});
15+
16+
console.log("Testing getLastTransactions with single API call optimization...\n");
17+
18+
(async () => {
19+
try {
20+
const startTime = Date.now();
21+
const lastTransactions = await client.getLastTransactions({});
22+
const endTime = Date.now();
23+
24+
console.log(`✅ Success! Fetched ${lastTransactions.length} transactions in ${endTime - startTime}ms\n`);
25+
console.log("Results:");
26+
console.log(JSON.stringify(lastTransactions, null, 2));
27+
} catch (error) {
28+
console.error("❌ Error:", error.message);
29+
console.error(error);
30+
}
31+
})();
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"scripts": {
3+
"start": "tsx index.ts"
4+
},
5+
"name": "get_last_transactions",
6+
"version": "1.0.0",
7+
"description": "",
8+
"main": "index.ts",
9+
"author": "",
10+
"license": "ISC"
11+
}

src/client/getLastTransactions.ts

Lines changed: 71 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { GetLastTransactionsInput } from "../internal";
33
import { LastTransaction } from "../types/transaction";
44

55
const INDEXER_BASE = "https://indexer.infra.truf.network";
6-
const RPC_URL = "https://gateway.mainnet.truf.network/rpc/v1";
76

87
export async function getLastTransactions(
98
kwilClient: WebKwil | NodeKwil,
@@ -24,64 +23,85 @@ export async function getLastTransactions(
2423
);
2524
const rows = (res.data?.result as { created_at: number; method: string }[]) || [];
2625

27-
// 2) build per-block Promises
28-
const tasks = rows.map(({ created_at, method }) => {
26+
if (rows.length === 0) {
27+
return [];
28+
}
29+
30+
// 2) Parse block heights and validate
31+
const blockHeights = rows.map(({ created_at }) => {
2932
const blockHeight = Number(created_at);
3033
if (Number.isNaN(blockHeight)) {
31-
return Promise.reject(new Error(`Invalid block height returned: ${created_at}`));
34+
throw new Error(`Invalid block height returned: ${created_at}`);
3235
}
36+
return blockHeight;
37+
});
3338

34-
const txUrl = `${INDEXER_BASE}/v0/chain/transactions`
35-
+ `?from-block=${blockHeight}&to-block=${blockHeight}`
36-
+ `&order=asc&limit=1`;
39+
// 3) Make a single range query to indexer for all blocks
40+
const minBlock = Math.min(...blockHeights);
41+
const maxBlock = Math.max(...blockHeights);
3742

38-
// INDEXER: always return a { sender, hash } object
39-
const txPromise = fetch(txUrl).then(async (resp) => {
40-
if (!resp.ok) throw new Error(`Indexer fetch failed: ${resp.status}`);
41-
const json = (await resp.json()) as {
42-
ok: boolean;
43-
data: Array<{ hash: string; sender: string }>;
44-
};
45-
if (!json.ok || json.data.length === 0) {
46-
return { sender: "(unknown)", hash: "(unknown)" };
47-
}
48-
const { hash, sender } = json.data[0];
49-
return { sender, hash };
50-
});
43+
const txUrl = `${INDEXER_BASE}/v0/chain/transactions`
44+
+ `?from-block=${minBlock}&to-block=${maxBlock}`
45+
+ `&order=asc`;
5146

52-
// RPC: get stamp_ms
53-
const rpcPromise = fetch(RPC_URL, {
54-
method: "POST",
55-
headers: { "Content-Type": "application/json" },
56-
body: JSON.stringify({
57-
jsonrpc: "2.0",
58-
method: "chain.block",
59-
params: { height: blockHeight },
60-
id: 1,
61-
}),
62-
}).then(async (resp) => {
63-
if (!resp.ok) {
64-
const txt = await resp.text();
65-
throw new Error(`RPC fetch failed: ${resp.status}${txt}`);
66-
}
67-
const rpc = (await resp.json()) as {
68-
result: { block: { header: { stamp_ms: number } } };
69-
};
70-
return rpc.result.block.header.stamp_ms;
71-
});
47+
const resp = await fetch(txUrl);
48+
if (!resp.ok) {
49+
throw new Error(`Indexer fetch failed: ${resp.status}`);
50+
}
7251

73-
// wait for both
74-
return Promise.all([txPromise, rpcPromise]).then(
75-
([{ sender, hash }, stampMs]) => ({
52+
const json = (await resp.json()) as {
53+
ok: boolean;
54+
data: Array<{
55+
block_height: number;
56+
hash: string;
57+
sender: string;
58+
stamp_ms: number | null;
59+
}>;
60+
};
61+
62+
if (!json.ok) {
63+
throw new Error("Indexer returned ok: false");
64+
}
65+
66+
// 4) Build a map of blockHeight -> first transaction
67+
const blockToTx = new Map<number, {
68+
hash: string;
69+
sender: string;
70+
stamp_ms: number | null;
71+
}>();
72+
73+
for (const tx of json.data) {
74+
// Only keep the first transaction per block
75+
if (!blockToTx.has(tx.block_height)) {
76+
blockToTx.set(tx.block_height, {
77+
hash: tx.hash,
78+
sender: tx.sender,
79+
stamp_ms: tx.stamp_ms,
80+
});
81+
}
82+
}
83+
84+
// 5) Map back to original order with methods from SQL query
85+
return rows.map(({ created_at, method }) => {
86+
const blockHeight = Number(created_at);
87+
const tx = blockToTx.get(blockHeight);
88+
89+
if (!tx) {
90+
return {
7691
blockHeight,
7792
method,
78-
sender,
79-
transactionHash: hash,
80-
stampMs,
81-
})
82-
);
83-
});
93+
sender: "(unknown)",
94+
transactionHash: "(unknown)",
95+
stampMs: 0,
96+
};
97+
}
8498

85-
// 3) await all in parallel
86-
return Promise.all(tasks);
99+
return {
100+
blockHeight,
101+
method,
102+
sender: tx.sender,
103+
transactionHash: tx.hash,
104+
stampMs: tx.stamp_ms ?? 0,
105+
};
106+
});
87107
}

0 commit comments

Comments
 (0)