@indexing/jiti 0.1.12 → 0.1.14

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/dist/main.js CHANGED
@@ -3,9 +3,14 @@ var $8zHUo$viem = require("viem");
3
3
  var $8zHUo$tronweb = require("tronweb");
4
4
  var $8zHUo$cosmjsprotosigning = require("@cosmjs/proto-signing");
5
5
  var $8zHUo$cosmjsstargate = require("@cosmjs/stargate");
6
+ var $8zHUo$toncore = require("@ton/core");
6
7
  var $8zHUo$bs58 = require("bs58");
7
8
 
8
9
 
10
+ function $parcel$export(e, n, v, s) {
11
+ Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
12
+ }
13
+
9
14
  function $parcel$exportWildcard(dest, source) {
10
15
  Object.keys(source).forEach(function(key) {
11
16
  if (key === 'default' || key === '__esModule' || Object.prototype.hasOwnProperty.call(dest, key)) {
@@ -23,18 +28,122 @@ function $parcel$exportWildcard(dest, source) {
23
28
  return dest;
24
29
  }
25
30
 
26
- function $parcel$export(e, n, v, s) {
27
- Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
28
- }
29
-
30
31
  function $parcel$interopDefault(a) {
31
32
  return a && a.__esModule ? a.default : a;
32
33
  }
33
34
 
35
+ var $parcel$global = globalThis;
36
+
37
+ var $parcel$modules = {};
38
+ var $parcel$inits = {};
39
+
40
+ var parcelRequire = $parcel$global["parcelRequire3fdc"];
41
+
42
+ if (parcelRequire == null) {
43
+ parcelRequire = function(id) {
44
+ if (id in $parcel$modules) {
45
+ return $parcel$modules[id].exports;
46
+ }
47
+ if (id in $parcel$inits) {
48
+ var init = $parcel$inits[id];
49
+ delete $parcel$inits[id];
50
+ var module = {id: id, exports: {}};
51
+ $parcel$modules[id] = module;
52
+ init.call(module.exports, module, module.exports);
53
+ return module.exports;
54
+ }
55
+ var err = new Error("Cannot find module '" + id + "'");
56
+ err.code = 'MODULE_NOT_FOUND';
57
+ throw err;
58
+ };
59
+
60
+ parcelRequire.register = function register(id, init) {
61
+ $parcel$inits[id] = init;
62
+ };
63
+
64
+ $parcel$global["parcelRequire3fdc"] = parcelRequire;
65
+ }
66
+
67
+ var parcelRegister = parcelRequire.register;
68
+ parcelRegister("lCdWD", function(module, exports) {
69
+
70
+ $parcel$export(module.exports, "createPostgresCacheStorage", () => createPostgresCacheStorage);
71
+ function createPostgresCacheStorage(opts) {
72
+ const table = (opts.table ?? "jiti_cache").replace(/[^a-zA-Z0-9_]/g, "");
73
+ let pool = opts.pool;
74
+ function getPool() {
75
+ if (!pool) {
76
+ // Obscure the require so jiti's bundler leaves `pg` external (loaded from the consumer).
77
+ const requirePg = eval("require");
78
+ const pg = requirePg("pg");
79
+ pool = new pg.Pool({
80
+ connectionString: opts.connectionUri
81
+ });
82
+ }
83
+ return pool;
84
+ }
85
+ let ready;
86
+ function ensureTable() {
87
+ if (!ready) ready = (async ()=>{
88
+ const p = getPool();
89
+ await p.query(`CREATE TABLE IF NOT EXISTS ${table} (
90
+ namespace text NOT NULL,
91
+ key text NOT NULL,
92
+ value jsonb,
93
+ found boolean NOT NULL DEFAULT true,
94
+ resolved_at timestamptz NOT NULL DEFAULT now(),
95
+ expires_at timestamptz,
96
+ PRIMARY KEY (namespace, key)
97
+ );`);
98
+ await p.query(`CREATE INDEX IF NOT EXISTS ${table}_expires_idx ON ${table} (expires_at) WHERE expires_at IS NOT NULL;`);
99
+ })();
100
+ return ready;
101
+ }
102
+ return {
103
+ async get (namespace, key) {
104
+ await ensureTable();
105
+ const { rows } = await getPool().query(`SELECT value, found, expires_at FROM ${table} WHERE namespace = $1 AND key = $2`, [
106
+ namespace,
107
+ key
108
+ ]);
109
+ if (!rows.length) return null;
110
+ const row = rows[0];
111
+ return {
112
+ value: row.value ?? null,
113
+ found: row.found,
114
+ expiresAt: row.expires_at ? new Date(row.expires_at).getTime() : null
115
+ };
116
+ },
117
+ async set (namespace, key, row) {
118
+ await ensureTable();
119
+ await getPool().query(`INSERT INTO ${table} (namespace, key, value, found, resolved_at, expires_at)
120
+ VALUES ($1, $2, $3::jsonb, $4, now(), $5)
121
+ ON CONFLICT (namespace, key) DO UPDATE
122
+ SET value = EXCLUDED.value, found = EXCLUDED.found, resolved_at = now(), expires_at = EXCLUDED.expires_at`, [
123
+ namespace,
124
+ key,
125
+ row.value === null || row.value === undefined ? null : JSON.stringify(row.value),
126
+ row.found,
127
+ row.expiresAt ? new Date(row.expiresAt).toISOString() : null
128
+ ]);
129
+ }
130
+ };
131
+ }
132
+
133
+ });
134
+
135
+
34
136
  $parcel$export(module.exports, "utils", () => $882b6d93070905b3$export$eab97d15b1788b8d);
35
137
  $parcel$export(module.exports, "templates", () => $882b6d93070905b3$export$a8fc3402335b0b04);
36
138
  $parcel$export(module.exports, "getAllTemplates", () => $882b6d93070905b3$export$cceb5167b935aafb);
37
139
  $parcel$export(module.exports, "getTemplateByKey", () => $882b6d93070905b3$export$a07bfd14bbc36e4b);
140
+ $parcel$export(module.exports, "cacheGet", () => $45fec2af5a17964d$export$2aca74ef9665e35d);
141
+ $parcel$export(module.exports, "cacheSet", () => $45fec2af5a17964d$export$59c69e19e33761f6);
142
+ $parcel$export(module.exports, "registerCacheNamespace", () => $226820b1648da9ad$export$2b845901f37b4099);
143
+ $parcel$export(module.exports, "createPostgresCacheStorage", () => (parcelRequire("lCdWD")).createPostgresCacheStorage);
144
+ $parcel$export(module.exports, "lookupJettonWallet", () => $b2a2726ff5c26695$export$77ffdf974cb300ec);
145
+ $parcel$export(module.exports, "resolveJettonWalletData", () => $b2a2726ff5c26695$export$cef9e9799d863462);
146
+ $parcel$export(module.exports, "TON_JETTON_NAMESPACE", () => $b2a2726ff5c26695$export$a0a0f70252f1386f);
38
147
  var $d7167569386d0d4c$exports = {};
39
148
  var $56acd58307ebf8e6$exports = {};
40
149
 
@@ -1359,6 +1468,219 @@ const $5a4d345db3c04a51$export$d7ac970e8e789607 = {
1359
1468
 
1360
1469
 
1361
1470
 
1471
+ // Per-namespace policy registry. Namespaces differ only here: immutable vs TTL
1472
+ // (`ttlSeconds`), and resolve-on-miss vs externally-populated (presence of `resolve`).
1473
+ // jiti registers the namespaces it can resolve itself (e.g. ton-jetton-wallet); a consumer
1474
+ // may register its own (e.g. token-price with no resolver, populated via cacheSet).
1475
+ const $226820b1648da9ad$var$REGISTRY = new Map();
1476
+ function $226820b1648da9ad$export$2b845901f37b4099(namespace, policy) {
1477
+ $226820b1648da9ad$var$REGISTRY.set(namespace, policy);
1478
+ }
1479
+ function $226820b1648da9ad$export$d23e4dbc7b86eaca(namespace) {
1480
+ return $226820b1648da9ad$var$REGISTRY.get(namespace) ?? {};
1481
+ }
1482
+
1483
+
1484
+ // cache-or-lookup core. jiti owns the orchestration — in-process L1, single-flight, negative
1485
+ // caching, TTL/immutability, read-through/write-through — and the per-namespace resolvers.
1486
+ // The consumer injects only durable storage (see createPostgresCacheStorage for a ready-made
1487
+ // Postgres adapter). With no storage available, callers simply don't invoke cacheGet.
1488
+ const $45fec2af5a17964d$var$L1_MAX = 50000;
1489
+ const $45fec2af5a17964d$var$L1 = new Map();
1490
+ function $45fec2af5a17964d$var$l1Get(id) {
1491
+ const e = $45fec2af5a17964d$var$L1.get(id);
1492
+ if (!e) return undefined;
1493
+ if (e.expiresAt !== null && e.expiresAt <= Date.now()) {
1494
+ $45fec2af5a17964d$var$L1.delete(id);
1495
+ return undefined;
1496
+ }
1497
+ $45fec2af5a17964d$var$L1.delete(id);
1498
+ $45fec2af5a17964d$var$L1.set(id, e); // bump recency
1499
+ return e;
1500
+ }
1501
+ function $45fec2af5a17964d$var$l1Set(id, value, expiresAt) {
1502
+ if ($45fec2af5a17964d$var$L1.size >= $45fec2af5a17964d$var$L1_MAX) {
1503
+ const oldest = $45fec2af5a17964d$var$L1.keys().next().value;
1504
+ if (oldest !== undefined) $45fec2af5a17964d$var$L1.delete(oldest);
1505
+ }
1506
+ $45fec2af5a17964d$var$L1.set(id, {
1507
+ value: value,
1508
+ expiresAt: expiresAt
1509
+ });
1510
+ }
1511
+ const $45fec2af5a17964d$var$inflight = new Map();
1512
+ function $45fec2af5a17964d$var$cacheId(namespace, key) {
1513
+ return `${namespace} ${key}`;
1514
+ }
1515
+ async function $45fec2af5a17964d$export$2aca74ef9665e35d(storage, namespace, key) {
1516
+ const id = $45fec2af5a17964d$var$cacheId(namespace, key);
1517
+ const l1 = $45fec2af5a17964d$var$l1Get(id);
1518
+ if (l1) return l1.value;
1519
+ const existing = $45fec2af5a17964d$var$inflight.get(id);
1520
+ if (existing !== undefined) return existing;
1521
+ const p = (async ()=>{
1522
+ const row = await storage.get(namespace, key);
1523
+ if (row && (row.expiresAt === null || row.expiresAt > Date.now())) {
1524
+ const value = row.found ? row.value : null;
1525
+ $45fec2af5a17964d$var$l1Set(id, value, row.expiresAt);
1526
+ return value;
1527
+ }
1528
+ const policy = (0, $226820b1648da9ad$export$d23e4dbc7b86eaca)(namespace);
1529
+ if (!policy.resolve) return null; // externally-populated namespace: miss ⇒ null (no write)
1530
+ let resolved = null;
1531
+ try {
1532
+ resolved = await policy.resolve(key);
1533
+ } catch {
1534
+ // Transient resolver failure (e.g. RPC) — do NOT cache so it retries next time.
1535
+ return null;
1536
+ }
1537
+ const found = resolved !== null && resolved !== undefined;
1538
+ const negativeTtl = policy.negativeTtlSeconds ?? 3600;
1539
+ if (!found && negativeTtl === 0) return null; // negative caching disabled for this namespace
1540
+ const ttlSeconds = found ? policy.ttlSeconds : negativeTtl;
1541
+ const expiresAt = ttlSeconds === null || ttlSeconds === undefined ? null : Date.now() + ttlSeconds * 1000;
1542
+ await storage.set(namespace, key, {
1543
+ value: found ? resolved : null,
1544
+ found: found,
1545
+ expiresAt: expiresAt
1546
+ });
1547
+ $45fec2af5a17964d$var$l1Set(id, found ? resolved : null, expiresAt);
1548
+ return found ? resolved : null;
1549
+ })().finally(()=>$45fec2af5a17964d$var$inflight.delete(id));
1550
+ $45fec2af5a17964d$var$inflight.set(id, p);
1551
+ return p;
1552
+ }
1553
+ async function $45fec2af5a17964d$export$59c69e19e33761f6(storage, namespace, key, value, ttlSecondsOverride) {
1554
+ const policy = (0, $226820b1648da9ad$export$d23e4dbc7b86eaca)(namespace);
1555
+ const ttlSeconds = ttlSecondsOverride !== undefined ? ttlSecondsOverride : policy.ttlSeconds;
1556
+ const expiresAt = ttlSeconds === null || ttlSeconds === undefined ? null : Date.now() + ttlSeconds * 1000;
1557
+ await storage.set(namespace, key, {
1558
+ value: value,
1559
+ found: true,
1560
+ expiresAt: expiresAt
1561
+ });
1562
+ $45fec2af5a17964d$var$l1Set($45fec2af5a17964d$var$cacheId(namespace, key), value, expiresAt);
1563
+ }
1564
+
1565
+
1566
+
1567
+ const $b2a2726ff5c26695$export$a0a0f70252f1386f = "ton-jetton-wallet";
1568
+ const $b2a2726ff5c26695$var$ORBS_MANAGER_URL = "https://ton.access.orbs.network/mngr/nodes";
1569
+ const $b2a2726ff5c26695$var$ORBS_REFRESH_MS = 300000;
1570
+ let $b2a2726ff5c26695$var$orbsHosts = [];
1571
+ let $b2a2726ff5c26695$var$orbsRefreshedAt = 0;
1572
+ function $b2a2726ff5c26695$var$shuffled(hosts) {
1573
+ const a = [
1574
+ ...hosts
1575
+ ];
1576
+ for(let i = a.length - 1; i > 0; i--){
1577
+ const j = Math.floor(Math.random() * (i + 1));
1578
+ [a[i], a[j]] = [
1579
+ a[j],
1580
+ a[i]
1581
+ ];
1582
+ }
1583
+ return a;
1584
+ }
1585
+ async function $b2a2726ff5c26695$var$getHosts() {
1586
+ if ($b2a2726ff5c26695$var$orbsHosts.length && Date.now() - $b2a2726ff5c26695$var$orbsRefreshedAt < $b2a2726ff5c26695$var$ORBS_REFRESH_MS) return $b2a2726ff5c26695$var$shuffled($b2a2726ff5c26695$var$orbsHosts);
1587
+ try {
1588
+ const resp = await fetch($b2a2726ff5c26695$var$ORBS_MANAGER_URL, {
1589
+ signal: AbortSignal.timeout(8000)
1590
+ });
1591
+ const nodes = await resp.json();
1592
+ const healthy = nodes.filter((n)=>n.Healthy === "1" && n.Mngr?.health?.["v2-mainnet"]).map((n)=>`https://ton.access.orbs.network/${n.NodeId}/1/mainnet/toncenter-api-v2/jsonRPC`);
1593
+ if (healthy.length) {
1594
+ $b2a2726ff5c26695$var$orbsHosts = healthy;
1595
+ $b2a2726ff5c26695$var$orbsRefreshedAt = Date.now();
1596
+ }
1597
+ } catch {
1598
+ // keep prior cached set
1599
+ }
1600
+ return $b2a2726ff5c26695$var$shuffled($b2a2726ff5c26695$var$orbsHosts);
1601
+ }
1602
+ function $b2a2726ff5c26695$export$938b62d04991c792(addr) {
1603
+ try {
1604
+ return (0, $8zHUo$toncore.Address).parse(addr).toString({
1605
+ urlSafe: true,
1606
+ bounceable: true,
1607
+ testOnly: false
1608
+ });
1609
+ } catch {
1610
+ return addr;
1611
+ }
1612
+ }
1613
+ function $b2a2726ff5c26695$var$parseAddressFromStackEntry(entry) {
1614
+ // toncenter v2 runGetMethod returns slice/cell results as ['cell', { bytes: <base64 BoC> }].
1615
+ const value = entry?.[1];
1616
+ const b64 = typeof value === "string" ? value : value?.bytes;
1617
+ if (!b64) return null;
1618
+ return $b2a2726ff5c26695$export$938b62d04991c792((0, $8zHUo$toncore.Cell).fromBase64(b64).beginParse().loadAddress().toString());
1619
+ }
1620
+ async function $b2a2726ff5c26695$var$runGetMethod(host, address, method, stack) {
1621
+ const resp = await fetch(host, {
1622
+ method: "POST",
1623
+ headers: {
1624
+ "content-type": "application/json"
1625
+ },
1626
+ body: JSON.stringify({
1627
+ jsonrpc: "2.0",
1628
+ id: 1,
1629
+ method: "runGetMethod",
1630
+ params: {
1631
+ address: address,
1632
+ method: method,
1633
+ stack: stack
1634
+ }
1635
+ }),
1636
+ signal: AbortSignal.timeout(15000)
1637
+ });
1638
+ const json = await resp.json();
1639
+ if (!json.ok || !json.result) throw new Error(`TON runGetMethod ${method} error: ${json.error || "unknown"}`);
1640
+ return {
1641
+ exitCode: json.result.exit_code,
1642
+ stack: json.result.stack
1643
+ };
1644
+ }
1645
+ async function $b2a2726ff5c26695$export$cef9e9799d863462(wallet) {
1646
+ const hosts = await $b2a2726ff5c26695$var$getHosts();
1647
+ if (!hosts.length) throw new Error("TON: no Orbs hosts available for get_wallet_data");
1648
+ let sawDefiniteMiss = false;
1649
+ let lastErr;
1650
+ for(let attempt = 0; attempt < 2; attempt++){
1651
+ for (const host of hosts)try {
1652
+ const { exitCode: exitCode, stack: stack } = await $b2a2726ff5c26695$var$runGetMethod(host, wallet, "get_wallet_data", []);
1653
+ if (exitCode !== 0) {
1654
+ sawDefiniteMiss = true; // no get_wallet_data → not a jetton wallet
1655
+ continue;
1656
+ }
1657
+ const owner = $b2a2726ff5c26695$var$parseAddressFromStackEntry(stack[1]);
1658
+ const master = $b2a2726ff5c26695$var$parseAddressFromStackEntry(stack[2]);
1659
+ if (owner && master) return {
1660
+ owner: owner,
1661
+ master: master
1662
+ };
1663
+ sawDefiniteMiss = true;
1664
+ } catch (e) {
1665
+ lastErr = e;
1666
+ }
1667
+ }
1668
+ if (sawDefiniteMiss) return null; // definitive: not a jetton wallet → negative-cache
1669
+ throw new Error(`TON: get_wallet_data failed for ${wallet}: ${lastErr?.message}`); // transient → don't cache
1670
+ }
1671
+ async function $b2a2726ff5c26695$export$77ffdf974cb300ec(storage, wallet) {
1672
+ return await (0, $45fec2af5a17964d$export$2aca74ef9665e35d)(storage, $b2a2726ff5c26695$export$a0a0f70252f1386f, $b2a2726ff5c26695$export$938b62d04991c792(wallet));
1673
+ }
1674
+ // Register on import: immutable mapping, resolve-on-miss via get_wallet_data; non-jetton
1675
+ // accounts negative-cached for a day.
1676
+ (0, $226820b1648da9ad$export$2b845901f37b4099)($b2a2726ff5c26695$export$a0a0f70252f1386f, {
1677
+ resolve: (key)=>$b2a2726ff5c26695$export$cef9e9799d863462(key),
1678
+ ttlSeconds: null,
1679
+ negativeTtlSeconds: 86400
1680
+ });
1681
+
1682
+
1683
+
1362
1684
  const $2e46ec862e47c14f$export$400f08bfae9ee97f = {
1363
1685
  match: (block)=>(0, $6bd2ca253e883278$export$ae001c77434c5340)(block) === "RIPPLE",
1364
1686
  transform (block) {
@@ -3533,65 +3855,150 @@ const $11a2c5b0fd56c033$export$bc6c7ab7e0727dd7 = {
3533
3855
 
3534
3856
 
3535
3857
 
3858
+
3859
+
3860
+ var $cd430dd11c31e572$require$Buffer = $8zHUo$buffer.Buffer;
3861
+ // TEP-74 jetton `internal_transfer` op-code. This is the message a recipient's
3862
+ // jetton wallet receives when it is credited, so it is the canonical signal of a
3863
+ // jetton deposit landing — and the only jetton message whose executing account
3864
+ // (`tx.address`) is the recipient jetton wallet itself.
3865
+ const $cd430dd11c31e572$var$JETTON_INTERNAL_TRANSFER_OP = 0x178d4519;
3866
+ // Decode a jetton `internal_transfer` message body (a base64-encoded BoC).
3867
+ // Returns null when the body is absent, is not a BoC, or carries a different
3868
+ // op-code (text comments, other jetton ops, arbitrary contract calls) — the
3869
+ // caller then falls back to the native path. Body layout (TEP-74):
3870
+ // internal_transfer#0x178d4519 query_id:uint64 amount:(VarUInteger 16)
3871
+ // from:MsgAddress response_address:MsgAddress ...
3872
+ function $cd430dd11c31e572$var$decodeJettonInternalTransfer(body) {
3873
+ if (!body) return null;
3874
+ try {
3875
+ const slice = (0, $8zHUo$toncore.Cell).fromBoc($cd430dd11c31e572$require$Buffer.from(body, "base64"))[0].beginParse();
3876
+ if (slice.loadUint(32) !== $cd430dd11c31e572$var$JETTON_INTERNAL_TRANSFER_OP) return null;
3877
+ slice.loadUintBig(64); // query_id
3878
+ const amount = slice.loadCoins();
3879
+ // `from` is the sending owner, read best-effort: it can be addr_none on some
3880
+ // mints/airdrops, and rare non-standard address encodings can throw. In
3881
+ // either case keep the (valid) amount and let the caller fall back to the
3882
+ // message source rather than dropping a real jetton transfer.
3883
+ let from;
3884
+ try {
3885
+ const fromAddress = slice.loadMaybeAddress();
3886
+ from = fromAddress ? fromAddress.toString({
3887
+ urlSafe: true,
3888
+ bounceable: true,
3889
+ testOnly: false
3890
+ }) : undefined;
3891
+ } catch {
3892
+ from = undefined;
3893
+ }
3894
+ return {
3895
+ amount: amount,
3896
+ from: from
3897
+ };
3898
+ } catch {
3899
+ return null;
3900
+ }
3901
+ }
3536
3902
  const $cd430dd11c31e572$export$608f3f42810b9879 = {
3537
3903
  match: (block)=>(0, $6bd2ca253e883278$export$ae001c77434c5340)(block) === "TON",
3538
3904
  transform (block) {
3539
- let transfers = [];
3905
+ const transfers = [];
3540
3906
  const typedBlock = block;
3541
3907
  const blockNumber = typedBlock.seqno;
3542
- const blockTimestamp = new Date(typedBlock.shards?.[0]?.gen_utime * 1000).toISOString();
3543
3908
  for (const shard of typedBlock.shards || [])for (const tx of shard.transactions || []){
3544
- const transactionLT = tx.transaction_id.lt;
3545
- const transactionHash = tx.transaction_id.hash;
3546
- const transactionFee = BigInt(tx.fee || "0");
3547
- const inVal = BigInt(tx.in_msg?.value || "0");
3548
- if (inVal > 0n) transfers.push({
3909
+ const inMsg = tx.in_msg;
3910
+ if (!inMsg) continue;
3911
+ const transactionHash = tx.transaction_id?.hash;
3912
+ const transactionGasFee = BigInt(tx.fee || "0");
3913
+ // Per-transaction time. shards[0].gen_utime — the previous source — is
3914
+ // wrong for any transaction outside the first shard and is undefined for
3915
+ // empty shards (producing `Invalid Date`).
3916
+ const timestamp = new Date((tx.utime || 0) * 1000).toISOString();
3917
+ // The account this transaction executed on: the recipient account for a
3918
+ // native transfer, or the recipient's jetton wallet for a jetton one
3919
+ // (== in_msg.destination).
3920
+ const to = tx.address?.account_address;
3921
+ // Walk `in_msg` only: every transfer is recorded once, at the
3922
+ // recipient's transaction, with `from` carried on the row so sender
3923
+ // queries still match. Walking `out_msgs` too would double-emit each hop
3924
+ // — the sender's and the recipient's transactions both fall in range.
3925
+ const jetton = $cd430dd11c31e572$var$decodeJettonInternalTransfer(inMsg.msg_data?.body);
3926
+ if (jetton) {
3927
+ // Jetton and native value are mutually exclusive per message: a jetton
3928
+ // `internal_transfer` also carries forwarded gas as `in_msg.value`, so
3929
+ // emitting a native transfer here too would surface a phantom TON hop.
3930
+ transfers.push({
3931
+ blockNumber: blockNumber,
3932
+ from: jetton.from || inMsg.source?.account_address,
3933
+ to: to,
3934
+ amount: jetton.amount,
3935
+ // The jetton master is not carried in the wallet-to-wallet
3936
+ // `internal_transfer`; a stateless template can't resolve it without
3937
+ // an RPC call, so `token` is intentionally left undefined.
3938
+ tokenType: "TOKEN",
3939
+ timestamp: timestamp,
3940
+ transactionHash: transactionHash,
3941
+ transactionGasFee: transactionGasFee
3942
+ });
3943
+ continue;
3944
+ }
3945
+ const inValue = BigInt(inMsg.value || "0");
3946
+ if (inValue > 0n) transfers.push({
3549
3947
  blockNumber: blockNumber,
3550
- from: tx.in_msg?.source?.account_address,
3551
- to: tx.address?.account_address,
3552
- amount: inVal,
3948
+ from: inMsg.source?.account_address,
3949
+ to: to,
3950
+ amount: inValue,
3553
3951
  token: "TON",
3554
3952
  tokenType: "NATIVE",
3555
- timestamp: blockTimestamp,
3953
+ timestamp: timestamp,
3556
3954
  transactionHash: transactionHash,
3557
- transactionGasFee: transactionFee
3955
+ transactionGasFee: transactionGasFee
3558
3956
  });
3559
- for (const outMsg of tx.out_msgs || []){
3560
- const outVal = BigInt(outMsg.value || "0");
3561
- if (outVal > 0n) transfers.push({
3562
- blockNumber: blockNumber,
3563
- from: outMsg.source?.account_address,
3564
- to: outMsg.destination?.account_address,
3565
- amount: outVal,
3566
- token: "TON",
3567
- tokenType: "NATIVE",
3568
- timestamp: blockTimestamp,
3569
- transactionHash: transactionHash,
3570
- transactionGasFee: transactionFee
3571
- });
3572
- }
3573
3957
  }
3574
3958
  return transfers;
3575
3959
  },
3576
3960
  tests: [
3577
3961
  {
3962
+ // Native TON transfer — `in_msg` carries value and no jetton body.
3578
3963
  params: {
3579
3964
  network: "TON",
3580
- walletAddress: "EQAFukUyzmHjUvOYDOjNE-wbZFFl2FWas1rFJoh8IiTsWD40",
3581
- contractAddress: ""
3965
+ transactionHash: "rwDRQeWniNdxBayFcDavE/Wo35B+MsfO4lJJmvz5Edk="
3582
3966
  },
3583
- payload: "https://jiti.indexing.co/networks/ton/44919328",
3967
+ payload: "https://jiti.indexing.co/networks/ton/71399000",
3584
3968
  output: [
3585
3969
  {
3586
- blockNumber: 44919328,
3587
- from: "EQAFukUyzmHjUvOYDOjNE-wbZFFl2FWas1rFJoh8IiTsWD40",
3588
- to: "EQCFTFAHOU3vFt2NiZhRD5dwuS0k7GS59vIg3WfCKwfaQGW2",
3589
- amount: 10000000n,
3970
+ blockNumber: 71399000,
3971
+ from: "EQBkhlXTyZjri7SfyZcgFbRkjlLvq4kU3UjBTtiTtwLITn0H",
3972
+ to: "EQABGo8KCza3ea8DNHMnSWZmbRzW-05332eTdfvW-XDQEjQM",
3973
+ amount: 203686310466n,
3590
3974
  token: "TON",
3591
3975
  tokenType: "NATIVE",
3592
- timestamp: "2025-02-13T23:10:18.000Z",
3593
- transactionHash: "Vh5cWr2uvCsdhoouBQ+EiUcF54os9oqvh8A/62EroQc=",
3594
- transactionGasFee: 2355233n
3976
+ timestamp: "2026-06-05T15:31:47.000Z",
3977
+ transactionHash: "rwDRQeWniNdxBayFcDavE/Wo35B+MsfO4lJJmvz5Edk=",
3978
+ transactionGasFee: 6668n
3979
+ }
3980
+ ]
3981
+ },
3982
+ {
3983
+ // Jetton transfer — `in_msg` is an internal_transfer (op 0x178d4519). The
3984
+ // message also carries 49740664 nanoTON of forwarded gas, which must NOT
3985
+ // surface as a phantom native transfer. `token` is left undefined (the
3986
+ // jetton master is not in the wallet-to-wallet message).
3987
+ params: {
3988
+ network: "TON",
3989
+ transactionHash: "Tx+nOxUzqo8kYpNkX20C2OTg6Dd9d6MHadQsknv4620="
3990
+ },
3991
+ payload: "https://jiti.indexing.co/networks/ton/71399000",
3992
+ output: [
3993
+ {
3994
+ blockNumber: 71399000,
3995
+ from: "EQDshc_H_RAJNi5CG1oBQfUQ1lQBB6tz54Kf2h756Xp14_Cv",
3996
+ to: "EQBMzIc7YUB_WkSkmaIooZyONQDayNfipt3PQ4IJRsGXsPGI",
3997
+ amount: 2800000000n,
3998
+ tokenType: "TOKEN",
3999
+ timestamp: "2026-06-05T15:31:47.000Z",
4000
+ transactionHash: "Tx+nOxUzqo8kYpNkX20C2OTg6Dd9d6MHadQsknv4620=",
4001
+ transactionGasFee: 233386n
3595
4002
  }
3596
4003
  ]
3597
4004
  }
@@ -3689,6 +4096,27 @@ const $7dd402f6ad0dab6a$var$UNIVERSAL_SUB_TEMPLATES = [
3689
4096
  (0, $f9ab50a3e879ac1c$export$b5fd4920e8b7d913),
3690
4097
  (0, $8deaea1ef39b6485$export$5beebc5708fabf3c)
3691
4098
  ];
4099
+ // Drops zero-amount + duplicate transfers and applies the optional contractAddress /
4100
+ // walletAddress / transactionHash param filters. Runs AFTER any jetton enrichment so the
4101
+ // filters see the resolved owner/master.
4102
+ function $7dd402f6ad0dab6a$var$applyTransferFilters(transfers, _ctx) {
4103
+ const seenTransfers = new Set();
4104
+ return transfers.filter((txfer)=>{
4105
+ if (txfer.amount <= BigInt(0)) return false;
4106
+ if (_ctx?.params) {
4107
+ if (_ctx.params.contractAddress && _ctx.params.contractAddress !== txfer.token) return false;
4108
+ if (_ctx.params.walletAddress && ![
4109
+ txfer.from,
4110
+ txfer.to
4111
+ ].includes(_ctx.params.walletAddress)) return false;
4112
+ if (_ctx.params.transactionHash && txfer.transactionHash !== _ctx.params.transactionHash) return false;
4113
+ }
4114
+ const key = `${txfer.transactionHash}-${txfer.from}-${txfer.to}-${txfer.amount}-${txfer.token}-${txfer.index}`;
4115
+ if (seenTransfers.has(key)) return false;
4116
+ seenTransfers.add(key);
4117
+ return true;
4118
+ });
4119
+ }
3692
4120
  const $7dd402f6ad0dab6a$var$tokenTransfersTemplate = {
3693
4121
  key: "token_transfers",
3694
4122
  name: "Token Transfers",
@@ -3741,23 +4169,27 @@ const $7dd402f6ad0dab6a$var$tokenTransfersTemplate = {
3741
4169
  transfers = sub.transform(block, _ctx);
3742
4170
  break;
3743
4171
  }
3744
- const seenTransfers = new Set();
3745
- transfers = transfers.filter((txfer)=>{
3746
- if (txfer.amount <= BigInt(0)) return false;
3747
- if (_ctx?.params) {
3748
- if (_ctx.params.contractAddress && _ctx.params.contractAddress !== txfer.token) return false;
3749
- if (_ctx.params.walletAddress && ![
3750
- txfer.from,
3751
- txfer.to
3752
- ].includes(_ctx.params.walletAddress)) return false;
3753
- if (_ctx.params.transactionHash && txfer.transactionHash !== _ctx.params.transactionHash) return false;
3754
- }
3755
- const key = `${txfer.transactionHash}-${txfer.from}-${txfer.to}-${txfer.amount}-${txfer.token}-${txfer.index}`;
3756
- if (seenTransfers.has(key)) return false;
3757
- seenTransfers.add(key);
3758
- return true;
3759
- });
3760
- return transfers;
4172
+ // Optional enrichment for account-model token transfers (TON jettons) where the block
4173
+ // only carries the token-WALLET address and no master: they come out as
4174
+ // `tokenType: 'TOKEN'`, `to` = wallet, `token` = undefined. When the caller injects a
4175
+ // `_ctx.cacheStorage` (durable cache), jiti resolves the wallet → { owner, master } via
4176
+ // its own cache+RPC and rewrites `to`/`token` BEFORE filtering, so contractAddress /
4177
+ // walletAddress filters match the real owner + master. jiti owns the cache + resolver
4178
+ // (get_wallet_data over Orbs); the consumer injects only storage. Async only when storage
4179
+ // is provided; the sync path is unchanged for every existing caller.
4180
+ const cacheStorage = _ctx?.cacheStorage;
4181
+ if (cacheStorage) return (async ()=>{
4182
+ await Promise.all(transfers.map(async (txfer)=>{
4183
+ if (txfer.tokenType !== "TOKEN" || txfer.token || !txfer.to) return;
4184
+ const info = await (0, $b2a2726ff5c26695$export$77ffdf974cb300ec)(cacheStorage, txfer.to);
4185
+ if (info) {
4186
+ txfer.to = info.owner;
4187
+ txfer.token = info.master;
4188
+ }
4189
+ }));
4190
+ return $7dd402f6ad0dab6a$var$applyTransferFilters(transfers, _ctx);
4191
+ })();
4192
+ return $7dd402f6ad0dab6a$var$applyTransferFilters(transfers, _ctx);
3761
4193
  },
3762
4194
  tests: $7dd402f6ad0dab6a$var$SUB_TEMPLATES.concat($7dd402f6ad0dab6a$var$UNIVERSAL_SUB_TEMPLATES).map((v)=>v.tests).flat()
3763
4195
  };
@@ -11514,6 +11946,10 @@ $parcel$exportWildcard($b4b970cc11bce2a5$exports, $4828ba704a8a504f$exports);
11514
11946
  $parcel$exportWildcard($b4b970cc11bce2a5$exports, $4a8e5fd52443a474$exports);
11515
11947
 
11516
11948
 
11949
+
11950
+
11951
+ var $lCdWD = parcelRequire("lCdWD");
11952
+
11517
11953
  const $882b6d93070905b3$export$eab97d15b1788b8d = {
11518
11954
  ...$d7167569386d0d4c$exports
11519
11955
  };