@indexing/jiti 0.1.18 → 0.1.20

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/module.js CHANGED
@@ -677,85 +677,82 @@ const $bea1ed486f96b980$export$ace043a4f2efe476 = {
677
677
  if (!tx?.events || !Array.isArray(tx.events)) continue;
678
678
  const timestamp = tx.timestamp ? new Date(parseInt(tx.timestamp, 10) / 1000).toISOString() : null;
679
679
  const gasUsed = BigInt(tx.gas_used || "0");
680
- const transfersByKey = {};
681
- const sender = tx.sender;
682
- const accountAddressMap = {};
683
- const changes = tx.changes;
684
- for(let ci = 0; ci < changes.length; ci += 1){
685
- if (!changes[ci]?.data) continue;
686
- const { address: accountAddress, data: { data: changeData, type: changeType } } = changes[ci];
687
- const { balance: balance, metadata: metadata, owner: owner } = changeData || {};
688
- if (changeType === "0x1::fungible_asset::FungibleStore") {
689
- if (!accountAddressMap[accountAddress]) accountAddressMap[accountAddress] = changes[ci + 1]?.data?.data?.owner;
690
- const to = accountAddressMap[accountAddress];
691
- const tokenAddress = metadata?.inner;
692
- if (!to || !tokenAddress || to === sender) continue;
680
+ const legs = {};
681
+ const changes = tx.changes || [];
682
+ // Fungible-asset Deposit/Withdraw events carry only the fungible-store
683
+ // object address, not its owner (guid.account_address is 0x0 and older
684
+ // events omit store_owner). The owner is recoverable from the store's
685
+ // 0x1::object::ObjectCore change, present in the same tx — build a
686
+ // store -> owner map so the events pass below resolves real accounts.
687
+ const storeOwner = {};
688
+ for (const change of changes)if (change?.data?.type === "0x1::object::ObjectCore" && change.data.data?.owner) storeOwner[change.address] = change.data.data.owner;
689
+ // Group canonical Withdraw + Deposit half-legs by (token, amount). Only
690
+ // the framework's own 0x1::fungible_asset / 0x1::coin events are
691
+ // authoritative an asset module may ALSO emit its own
692
+ // <module>::{Withdraw,Deposit} for the same movement (e.g. a stablecoin),
693
+ // which must not be double-counted. Excluding those non-framework
694
+ // duplicates here (rather than relying on the (token, amount) bucket to
695
+ // collapse them) means each bucket holds one entry per REAL half-leg, so N
696
+ // distinct transfers of the same asset + amount in one tx are preserved
697
+ // instead of collapsed into one.
698
+ const canonical = /^0x1::(fungible_asset::(Withdraw|Deposit)|coin::(Withdraw|Deposit)Event)$/;
699
+ for (const evt of tx.events){
700
+ const evtType = evt.type;
701
+ if (!canonical.test(evtType)) continue;
702
+ const data = evt.data;
703
+ const amount = data.amount;
704
+ const accountAddr = data.store_owner || (data.store ? storeOwner[data.store] : undefined) || evt.guid?.account_address || "";
705
+ // Resolve the asset id. Fungible-asset events carry a `store` whose
706
+ // metadata object is the asset; legacy coin events carry no store — the
707
+ // coin type is the <T> of the owner's 0x1::coin::CoinStore<T> change.
708
+ // Defaulting to APT only when neither resolves avoids mislabeling a
709
+ // non-APT legacy coin as native.
710
+ let tokenAddr = "0x1::aptos_coin::AptosCoin";
711
+ if (data.store) tokenAddr = changes.find((c)=>c.address === data.store && c.data.type === "0x1::fungible_asset::FungibleStore")?.data?.data?.metadata?.inner || data.store;
712
+ else {
713
+ const coinType = changes.find((c)=>c.address === accountAddr && /^0x1::coin::CoinStore<.+>$/.test(c.data?.type ?? ""))?.data?.type?.match(/^0x1::coin::CoinStore<(.+)>$/)?.[1];
714
+ if (coinType) tokenAddr = coinType;
715
+ }
716
+ const key = `${tokenAddr}-${amount}`;
717
+ if (!legs[key]) legs[key] = {
718
+ token: tokenAddr,
719
+ amount: amount,
720
+ froms: [],
721
+ tos: []
722
+ };
723
+ if (evtType.includes("Withdraw")) legs[key].froms.push(accountAddr);
724
+ else legs[key].tos.push(accountAddr);
725
+ }
726
+ for (const leg of Object.values(legs)){
727
+ // Native APT is the legacy coin (0x1::aptos_coin::AptosCoin) and its
728
+ // migrated fungible-asset metadata object (0xa / its zero-padded long
729
+ // form): both normalize to a null token with tokenType NATIVE. Every
730
+ // other asset keeps its (lowercased) address.
731
+ const tokenAddrLower = leg.token.toLowerCase();
732
+ const isNative = tokenAddrLower.includes("aptos_coin") || tokenAddrLower.replace(/^0x/, "").replace(/^0+/, "") === "a";
733
+ // Pair each withdrawal with a deposit of the same (token, amount), in
734
+ // event order. A balanced transfer has one of each; a one-sender→many or
735
+ // many→one-recipient batch pairs index-wise. Half-legs with no
736
+ // counterpart (mints, burns, fee-only legs) fall outside the paired
737
+ // range and are dropped.
738
+ const pairCount = Math.min(leg.froms.length, leg.tos.length);
739
+ for(let i = 0; i < pairCount; i += 1){
740
+ const from = leg.froms[i];
741
+ const to = leg.tos[i];
742
+ if (!from || !to || to === "0x00") continue;
693
743
  transfers.push({
694
- amount: BigInt(balance),
744
+ amount: BigInt(leg.amount || 0),
695
745
  blockNumber: parseInt(typedBlock.block_height, 10),
696
- from: sender,
746
+ from: from,
697
747
  to: to,
698
748
  timestamp: timestamp,
699
- token: tokenAddress,
700
- tokenType: "TOKEN",
749
+ token: isNative ? null : tokenAddrLower || null,
750
+ tokenType: isNative ? "NATIVE" : "TOKEN",
701
751
  transactionGasFee: gasUsed,
702
752
  transactionHash: tx.hash
703
753
  });
704
- } else if (owner?.length > 4) {
705
- const payload = tx.payload;
706
- if (payload && payload.function === "0x1::aptos_account::transfer_coins") {
707
- const coinType = payload.type_arguments?.[0];
708
- const isNative = !coinType || coinType.toLowerCase().includes("aptos_coin");
709
- transfers.push({
710
- amount: BigInt(payload.arguments[1]),
711
- blockNumber: parseInt(typedBlock.block_height, 10),
712
- from: sender,
713
- to: owner,
714
- timestamp: timestamp,
715
- token: isNative ? null : coinType.toLowerCase(),
716
- tokenType: isNative ? "NATIVE" : "TOKEN",
717
- transactionGasFee: gasUsed,
718
- transactionHash: tx.hash
719
- });
720
- }
721
- }
722
- }
723
- for (const evt of tx.events){
724
- const evtType = evt.type;
725
- if (/::(Withdraw|Deposit)[^:]*/.test(evtType)) {
726
- const data = evt.data;
727
- const amount = data.amount;
728
- const accountAddr = data.store_owner || evt.guid?.account_address || "";
729
- let tokenAddr = "0x1::aptos_coin::AptosCoin";
730
- if (data.store) tokenAddr = tx.changes.find((c)=>c.address === data.store && c.data.type === "0x1::fungible_asset::FungibleStore")?.data?.data?.metadata?.inner || data.store;
731
- const compositeKey = `${tx.hash}-${tokenAddr}-${amount}`;
732
- if (!transfersByKey[compositeKey]) transfersByKey[compositeKey] = {
733
- amount: amount,
734
- tokenAddress: tokenAddr
735
- };
736
- if (/::Withdraw[^:]*/.test(evtType)) transfersByKey[compositeKey].from = accountAddr;
737
- else transfersByKey[compositeKey].to = accountAddr;
738
754
  }
739
755
  }
740
- for (const partial of Object.values(transfersByKey)){
741
- if (!partial.from || !partial.to) continue;
742
- if (partial.to === "0x00") continue;
743
- let finalToken = null;
744
- let finalTokenType = "TOKEN";
745
- if (partial.tokenAddress?.toLowerCase().includes("aptos_coin")) finalToken = null;
746
- else finalToken = partial.tokenAddress?.toLowerCase();
747
- transfers.push({
748
- amount: BigInt(partial.amount || 0),
749
- blockNumber: parseInt(typedBlock.block_height, 10),
750
- from: partial.from,
751
- to: partial.to,
752
- timestamp: timestamp,
753
- token: finalToken,
754
- tokenType: finalTokenType,
755
- transactionGasFee: gasUsed,
756
- transactionHash: tx.hash
757
- });
758
- }
759
756
  }
760
757
  return transfers.filter((txfer)=>txfer.to?.length > 4).map((txfer)=>{
761
758
  const fromAddr = txfer.from.length < 66 ? `0x${txfer.from.slice(2).padStart(64, "0")}` : txfer.from;
@@ -782,7 +779,7 @@ const $bea1ed486f96b980$export$ace043a4f2efe476 = {
782
779
  to: "0x3b5d2e7e8da86903beb19d5a7135764aac812e18af193895d75f3a8f6a066cb0",
783
780
  timestamp: "2025-03-02T21:07:06.002Z",
784
781
  token: null,
785
- tokenType: "TOKEN",
782
+ tokenType: "NATIVE",
786
783
  transactionGasFee: 13n,
787
784
  transactionHash: "0xfbdef795d11df124cca264f3370b09fb04fb1c1d24a2d2e1df0693c096a76d13"
788
785
  },
@@ -793,7 +790,7 @@ const $bea1ed486f96b980$export$ace043a4f2efe476 = {
793
790
  to: "0x04b2b6bc8c2c5794c51607c962f482593f9b5ea09373a8ce249a1f799cca7a1e",
794
791
  timestamp: "2025-03-02T21:07:06.002Z",
795
792
  token: null,
796
- tokenType: "TOKEN",
793
+ tokenType: "NATIVE",
797
794
  transactionGasFee: 13n,
798
795
  transactionHash: "0xfbdef795d11df124cca264f3370b09fb04fb1c1d24a2d2e1df0693c096a76d13"
799
796
  }
@@ -806,17 +803,6 @@ const $bea1ed486f96b980$export$ace043a4f2efe476 = {
806
803
  },
807
804
  payload: "https://jiti.indexing.co/networks/aptos/303623631",
808
805
  output: [
809
- {
810
- amount: 3000160n,
811
- blockNumber: 303623631,
812
- from: "0xa4e7455d27731ab857e9701b1e6ed72591132b909fe6e4fd99b66c1d6318d9e8",
813
- timestamp: "2025-03-14T15:39:49.845Z",
814
- to: "0x9317336bfc9ba6987d40492ddea8d41e11b7c2e473f3556a9c82309d326e79ce",
815
- token: "0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b",
816
- tokenType: "TOKEN",
817
- transactionGasFee: 16n,
818
- transactionHash: "0x24b8854bad1f6543b35069eacd6ec40a583ca7fa452b422b04d747d24b65279c"
819
- },
820
806
  {
821
807
  amount: 1000060n,
822
808
  blockNumber: 303623631,
@@ -859,17 +845,6 @@ const $bea1ed486f96b980$export$ace043a4f2efe476 = {
859
845
  },
860
846
  payload: "https://jiti.indexing.co/networks/aptos/403873552",
861
847
  output: [
862
- {
863
- amount: 19500000n,
864
- blockNumber: 403873552,
865
- from: "0x8509aa39bc09ea530b0481c573c0215781c01fa363c135996614bb11cf337703",
866
- timestamp: "2025-08-11T11:07:54.286Z",
867
- to: "0x089556578008574ed3fddda6bc2ea6bee475b042e237bbb2f447c263086edcc5",
868
- token: "0xa",
869
- tokenType: "TOKEN",
870
- transactionGasFee: 18n,
871
- transactionHash: "0xa7d2d682f6940c1023d95ae8950b8d1dc0604abd34a4a2cc654f1be6f330ca44"
872
- },
873
848
  {
874
849
  amount: 9500000n,
875
850
  blockNumber: 403873552,
@@ -883,9 +858,12 @@ const $bea1ed486f96b980$export$ace043a4f2efe476 = {
883
858
  }
884
859
  ]
885
860
  },
886
- // transfer_coins with non-native token (PROPS) should NOT produce token: null
887
- // Bug: the transfer_coins code path blindly sets token: null, tokenType: 'NATIVE'
888
- // even when type_arguments indicates a non-native coin
861
+ // Migrated coin (PROPS) sent via aptos_account::transfer_coins. One real
862
+ // movement 18_740_000_000_000 from the sender to 0xf520…, reported once by
863
+ // the asset's fungible-asset metadata object (0x6dba…). Previously this
864
+ // produced three rows: a spurious self-transfer (to === sender), a
865
+ // post-balance amount (53_458_526_229_170 = the recipient's resulting
866
+ // balance), and a coin-type-named duplicate.
889
867
  {
890
868
  params: {
891
869
  network: "APTOS",
@@ -897,34 +875,451 @@ const $bea1ed486f96b980$export$ace043a4f2efe476 = {
897
875
  amount: 18740000000000n,
898
876
  blockNumber: 661127894,
899
877
  from: "0xaa0090c74e4976834ff1b9b9ef945e1c4b6cdb49cccf37c2554ef026081312f1",
900
- to: "0xaa0090c74e4976834ff1b9b9ef945e1c4b6cdb49cccf37c2554ef026081312f1",
878
+ to: "0xf520886f20b097e2e2e4116ab66d943f13a3f107d2ba09f6f1abc38e872b234c",
901
879
  timestamp: "2026-03-13T15:33:48.659Z",
902
- token: "0xe50684a338db732d8fb8a3ac71c4b8633878bd0193bca5de2ebc852a83b35099::propbase_coin::props",
880
+ token: "0x6dba1728c73363be1bdd4d504844c40fbb893e368ccbeff1d1bd83497dbc756d",
903
881
  tokenType: "TOKEN",
904
882
  transactionGasFee: 16n,
905
883
  transactionHash: "0xaaec78039e7392b430c554bc33291c2786a1c92669e8f8f88280f419b0792d29"
884
+ }
885
+ ]
886
+ },
887
+ // Legacy (unpaired) coin moved via 0x1::coin Withdraw/Deposit events: the
888
+ // events carry no fungible-store, so the asset is the <T> of the owner's
889
+ // 0x1::coin::CoinStore<T> change. A non-APT coin must surface as that TOKEN,
890
+ // not be mislabeled native. (Synthetic block — these are increasingly rare
891
+ // on mainnet as coins migrate to the fungible-asset standard.)
892
+ {
893
+ params: {
894
+ network: "APTOS"
895
+ },
896
+ payload: {
897
+ _network: "APTOS",
898
+ block_height: "999000001",
899
+ transactions: [
900
+ {
901
+ type: "user_transaction",
902
+ hash: "0x1111111111111111111111111111111111111111111111111111111111111111",
903
+ timestamp: "1700000000000000",
904
+ gas_used: "7",
905
+ sender: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1",
906
+ events: [
907
+ {
908
+ type: "0x1::coin::WithdrawEvent",
909
+ guid: {
910
+ account_address: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1"
911
+ },
912
+ data: {
913
+ amount: "500"
914
+ }
915
+ },
916
+ {
917
+ type: "0x1::coin::DepositEvent",
918
+ guid: {
919
+ account_address: "0xb2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2"
920
+ },
921
+ data: {
922
+ amount: "500"
923
+ }
924
+ }
925
+ ],
926
+ changes: [
927
+ {
928
+ address: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1",
929
+ data: {
930
+ type: "0x1::coin::CoinStore<0xe5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5::usdc::USDC>",
931
+ data: {}
932
+ }
933
+ },
934
+ {
935
+ address: "0xb2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2",
936
+ data: {
937
+ type: "0x1::coin::CoinStore<0xe5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5::usdc::USDC>",
938
+ data: {}
939
+ }
940
+ }
941
+ ]
942
+ }
943
+ ]
944
+ },
945
+ output: [
946
+ {
947
+ amount: 500n,
948
+ blockNumber: 999000001,
949
+ from: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1",
950
+ to: "0xb2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2",
951
+ timestamp: "2023-11-14T22:13:20.000Z",
952
+ token: "0xe5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5::usdc::usdc",
953
+ tokenType: "TOKEN",
954
+ transactionGasFee: 7n,
955
+ transactionHash: "0x1111111111111111111111111111111111111111111111111111111111111111"
956
+ }
957
+ ]
958
+ },
959
+ // Legacy APT moved via 0x1::coin events resolves to native (token null,
960
+ // tokenType NATIVE) — the CoinStore<T> is 0x1::aptos_coin::AptosCoin.
961
+ {
962
+ params: {
963
+ network: "APTOS"
964
+ },
965
+ payload: {
966
+ _network: "APTOS",
967
+ block_height: "999000002",
968
+ transactions: [
969
+ {
970
+ type: "user_transaction",
971
+ hash: "0x2222222222222222222222222222222222222222222222222222222222222222",
972
+ timestamp: "1700000000000000",
973
+ gas_used: "3",
974
+ sender: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1",
975
+ events: [
976
+ {
977
+ type: "0x1::coin::WithdrawEvent",
978
+ guid: {
979
+ account_address: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1"
980
+ },
981
+ data: {
982
+ amount: "900"
983
+ }
984
+ },
985
+ {
986
+ type: "0x1::coin::DepositEvent",
987
+ guid: {
988
+ account_address: "0xb2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2"
989
+ },
990
+ data: {
991
+ amount: "900"
992
+ }
993
+ }
994
+ ],
995
+ changes: [
996
+ {
997
+ address: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1",
998
+ data: {
999
+ type: "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>",
1000
+ data: {}
1001
+ }
1002
+ },
1003
+ {
1004
+ address: "0xb2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2",
1005
+ data: {
1006
+ type: "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>",
1007
+ data: {}
1008
+ }
1009
+ }
1010
+ ]
1011
+ }
1012
+ ]
1013
+ },
1014
+ output: [
1015
+ {
1016
+ amount: 900n,
1017
+ blockNumber: 999000002,
1018
+ from: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1",
1019
+ to: "0xb2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2",
1020
+ timestamp: "2023-11-14T22:13:20.000Z",
1021
+ token: null,
1022
+ tokenType: "NATIVE",
1023
+ transactionGasFee: 3n,
1024
+ transactionHash: "0x2222222222222222222222222222222222222222222222222222222222222222"
1025
+ }
1026
+ ]
1027
+ },
1028
+ // One sender → two recipients of the same fungible asset with DISTINCT
1029
+ // amounts in a single tx: each movement is its own row (no collapse, no
1030
+ // dropped transfer), with owners resolved from the stores' ObjectCore.
1031
+ {
1032
+ params: {
1033
+ network: "APTOS"
1034
+ },
1035
+ payload: {
1036
+ _network: "APTOS",
1037
+ block_height: "999000003",
1038
+ transactions: [
1039
+ {
1040
+ type: "user_transaction",
1041
+ hash: "0x3333333333333333333333333333333333333333333333333333333333333333",
1042
+ timestamp: "1700000000000000",
1043
+ gas_used: "5",
1044
+ sender: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1",
1045
+ events: [
1046
+ {
1047
+ type: "0x1::fungible_asset::Withdraw",
1048
+ guid: {
1049
+ account_address: "0x0"
1050
+ },
1051
+ data: {
1052
+ store: "0x5151515151515151515151515151515151515151515151515151515151515151",
1053
+ amount: "100"
1054
+ }
1055
+ },
1056
+ {
1057
+ type: "0x1::fungible_asset::Deposit",
1058
+ guid: {
1059
+ account_address: "0x0"
1060
+ },
1061
+ data: {
1062
+ store: "0x5252525252525252525252525252525252525252525252525252525252525252",
1063
+ amount: "100"
1064
+ }
1065
+ },
1066
+ {
1067
+ type: "0x1::fungible_asset::Withdraw",
1068
+ guid: {
1069
+ account_address: "0x0"
1070
+ },
1071
+ data: {
1072
+ store: "0x5151515151515151515151515151515151515151515151515151515151515151",
1073
+ amount: "200"
1074
+ }
1075
+ },
1076
+ {
1077
+ type: "0x1::fungible_asset::Deposit",
1078
+ guid: {
1079
+ account_address: "0x0"
1080
+ },
1081
+ data: {
1082
+ store: "0x5353535353535353535353535353535353535353535353535353535353535353",
1083
+ amount: "200"
1084
+ }
1085
+ }
1086
+ ],
1087
+ changes: [
1088
+ {
1089
+ address: "0x5151515151515151515151515151515151515151515151515151515151515151",
1090
+ data: {
1091
+ type: "0x1::fungible_asset::FungibleStore",
1092
+ data: {
1093
+ metadata: {
1094
+ inner: "0xd4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4"
1095
+ }
1096
+ }
1097
+ }
1098
+ },
1099
+ {
1100
+ address: "0x5252525252525252525252525252525252525252525252525252525252525252",
1101
+ data: {
1102
+ type: "0x1::fungible_asset::FungibleStore",
1103
+ data: {
1104
+ metadata: {
1105
+ inner: "0xd4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4"
1106
+ }
1107
+ }
1108
+ }
1109
+ },
1110
+ {
1111
+ address: "0x5353535353535353535353535353535353535353535353535353535353535353",
1112
+ data: {
1113
+ type: "0x1::fungible_asset::FungibleStore",
1114
+ data: {
1115
+ metadata: {
1116
+ inner: "0xd4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4"
1117
+ }
1118
+ }
1119
+ }
1120
+ },
1121
+ {
1122
+ address: "0x5151515151515151515151515151515151515151515151515151515151515151",
1123
+ data: {
1124
+ type: "0x1::object::ObjectCore",
1125
+ data: {
1126
+ owner: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1"
1127
+ }
1128
+ }
1129
+ },
1130
+ {
1131
+ address: "0x5252525252525252525252525252525252525252525252525252525252525252",
1132
+ data: {
1133
+ type: "0x1::object::ObjectCore",
1134
+ data: {
1135
+ owner: "0xb2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2"
1136
+ }
1137
+ }
1138
+ },
1139
+ {
1140
+ address: "0x5353535353535353535353535353535353535353535353535353535353535353",
1141
+ data: {
1142
+ type: "0x1::object::ObjectCore",
1143
+ data: {
1144
+ owner: "0xc3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3"
1145
+ }
1146
+ }
1147
+ }
1148
+ ]
1149
+ }
1150
+ ]
1151
+ },
1152
+ output: [
1153
+ {
1154
+ amount: 100n,
1155
+ blockNumber: 999000003,
1156
+ from: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1",
1157
+ to: "0xb2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2",
1158
+ timestamp: "2023-11-14T22:13:20.000Z",
1159
+ token: "0xd4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4",
1160
+ tokenType: "TOKEN",
1161
+ transactionGasFee: 5n,
1162
+ transactionHash: "0x3333333333333333333333333333333333333333333333333333333333333333"
906
1163
  },
907
1164
  {
908
- amount: 53458526229170n,
909
- blockNumber: 661127894,
910
- from: "0xaa0090c74e4976834ff1b9b9ef945e1c4b6cdb49cccf37c2554ef026081312f1",
911
- to: "0xf520886f20b097e2e2e4116ab66d943f13a3f107d2ba09f6f1abc38e872b234c",
912
- timestamp: "2026-03-13T15:33:48.659Z",
913
- token: "0x6dba1728c73363be1bdd4d504844c40fbb893e368ccbeff1d1bd83497dbc756d",
1165
+ amount: 200n,
1166
+ blockNumber: 999000003,
1167
+ from: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1",
1168
+ to: "0xc3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3",
1169
+ timestamp: "2023-11-14T22:13:20.000Z",
1170
+ token: "0xd4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4",
914
1171
  tokenType: "TOKEN",
915
- transactionGasFee: 16n,
916
- transactionHash: "0xaaec78039e7392b430c554bc33291c2786a1c92669e8f8f88280f419b0792d29"
1172
+ transactionGasFee: 5n,
1173
+ transactionHash: "0x3333333333333333333333333333333333333333333333333333333333333333"
1174
+ }
1175
+ ]
1176
+ },
1177
+ // Two transfers of the SAME asset AND SAME amount in one tx (an equal-split
1178
+ // batch send) must NOT collapse: the canonical half-legs are paired
1179
+ // index-wise, so each surfaces as its own row.
1180
+ {
1181
+ params: {
1182
+ network: "APTOS"
1183
+ },
1184
+ payload: {
1185
+ _network: "APTOS",
1186
+ block_height: "999000004",
1187
+ transactions: [
1188
+ {
1189
+ type: "user_transaction",
1190
+ hash: "0x4444444444444444444444444444444444444444444444444444444444444444",
1191
+ timestamp: "1700000000000000",
1192
+ gas_used: "6",
1193
+ sender: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1",
1194
+ events: [
1195
+ {
1196
+ type: "0x1::fungible_asset::Withdraw",
1197
+ guid: {
1198
+ account_address: "0x0"
1199
+ },
1200
+ data: {
1201
+ store: "0x5151515151515151515151515151515151515151515151515151515151515151",
1202
+ amount: "100"
1203
+ }
1204
+ },
1205
+ {
1206
+ type: "0x1::fungible_asset::Deposit",
1207
+ guid: {
1208
+ account_address: "0x0"
1209
+ },
1210
+ data: {
1211
+ store: "0x5252525252525252525252525252525252525252525252525252525252525252",
1212
+ amount: "100"
1213
+ }
1214
+ },
1215
+ {
1216
+ type: "0x1::fungible_asset::Withdraw",
1217
+ guid: {
1218
+ account_address: "0x0"
1219
+ },
1220
+ data: {
1221
+ store: "0x5151515151515151515151515151515151515151515151515151515151515151",
1222
+ amount: "100"
1223
+ }
1224
+ },
1225
+ {
1226
+ type: "0x1::fungible_asset::Deposit",
1227
+ guid: {
1228
+ account_address: "0x0"
1229
+ },
1230
+ data: {
1231
+ store: "0x5353535353535353535353535353535353535353535353535353535353535353",
1232
+ amount: "100"
1233
+ }
1234
+ }
1235
+ ],
1236
+ changes: [
1237
+ {
1238
+ address: "0x5151515151515151515151515151515151515151515151515151515151515151",
1239
+ data: {
1240
+ type: "0x1::fungible_asset::FungibleStore",
1241
+ data: {
1242
+ metadata: {
1243
+ inner: "0xd4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4"
1244
+ }
1245
+ }
1246
+ }
1247
+ },
1248
+ {
1249
+ address: "0x5252525252525252525252525252525252525252525252525252525252525252",
1250
+ data: {
1251
+ type: "0x1::fungible_asset::FungibleStore",
1252
+ data: {
1253
+ metadata: {
1254
+ inner: "0xd4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4"
1255
+ }
1256
+ }
1257
+ }
1258
+ },
1259
+ {
1260
+ address: "0x5353535353535353535353535353535353535353535353535353535353535353",
1261
+ data: {
1262
+ type: "0x1::fungible_asset::FungibleStore",
1263
+ data: {
1264
+ metadata: {
1265
+ inner: "0xd4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4"
1266
+ }
1267
+ }
1268
+ }
1269
+ },
1270
+ {
1271
+ address: "0x5151515151515151515151515151515151515151515151515151515151515151",
1272
+ data: {
1273
+ type: "0x1::object::ObjectCore",
1274
+ data: {
1275
+ owner: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1"
1276
+ }
1277
+ }
1278
+ },
1279
+ {
1280
+ address: "0x5252525252525252525252525252525252525252525252525252525252525252",
1281
+ data: {
1282
+ type: "0x1::object::ObjectCore",
1283
+ data: {
1284
+ owner: "0xb2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2"
1285
+ }
1286
+ }
1287
+ },
1288
+ {
1289
+ address: "0x5353535353535353535353535353535353535353535353535353535353535353",
1290
+ data: {
1291
+ type: "0x1::object::ObjectCore",
1292
+ data: {
1293
+ owner: "0xc3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3"
1294
+ }
1295
+ }
1296
+ }
1297
+ ]
1298
+ }
1299
+ ]
1300
+ },
1301
+ output: [
1302
+ {
1303
+ amount: 100n,
1304
+ blockNumber: 999000004,
1305
+ from: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1",
1306
+ to: "0xb2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2",
1307
+ timestamp: "2023-11-14T22:13:20.000Z",
1308
+ token: "0xd4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4",
1309
+ tokenType: "TOKEN",
1310
+ transactionGasFee: 6n,
1311
+ transactionHash: "0x4444444444444444444444444444444444444444444444444444444444444444"
917
1312
  },
918
1313
  {
919
- amount: 18740000000000n,
920
- blockNumber: 661127894,
921
- from: "0xaa0090c74e4976834ff1b9b9ef945e1c4b6cdb49cccf37c2554ef026081312f1",
922
- to: "0xf520886f20b097e2e2e4116ab66d943f13a3f107d2ba09f6f1abc38e872b234c",
923
- timestamp: "2026-03-13T15:33:48.659Z",
924
- token: "0xe50684a338db732d8fb8a3ac71c4b8633878bd0193bca5de2ebc852a83b35099::propbase_coin::props",
1314
+ amount: 100n,
1315
+ blockNumber: 999000004,
1316
+ from: "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1",
1317
+ to: "0xc3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3",
1318
+ timestamp: "2023-11-14T22:13:20.000Z",
1319
+ token: "0xd4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4",
925
1320
  tokenType: "TOKEN",
926
- transactionGasFee: 16n,
927
- transactionHash: "0xaaec78039e7392b430c554bc33291c2786a1c92669e8f8f88280f419b0792d29"
1321
+ transactionGasFee: 6n,
1322
+ transactionHash: "0x4444444444444444444444444444444444444444444444444444444444444444"
928
1323
  }
929
1324
  ]
930
1325
  }
@@ -1770,11 +2165,17 @@ const $07b3982e2fc4c8b2$export$400f08bfae9ee97f = {
1770
2165
  const deliveredOrAmount = typedTx.metaData?.delivered_amount ?? typedTx.Amount ?? "0";
1771
2166
  let tokenSymbol = "XRP";
1772
2167
  let tokenType = "NATIVE";
2168
+ // An issued currency is uniquely identified by (currency, issuer) — two "USD"
2169
+ // IOUs from different issuers are distinct tokens. Emit the token id as
2170
+ // `<currency>.<issuer>` so consumers can match by issuer; native XRP has no
2171
+ // issuer and keeps the bare "XRP" symbol.
2172
+ let issuer;
1773
2173
  let parsedAmount;
1774
2174
  // Only issued currencies carry an explicit scale; native XRP stays in integer drops.
1775
2175
  let decimals;
1776
2176
  if (typeof deliveredOrAmount === "object") {
1777
2177
  tokenSymbol = $07b3982e2fc4c8b2$var$decodeXrplCurrency(deliveredOrAmount.currency);
2178
+ issuer = deliveredOrAmount.issuer;
1778
2179
  tokenType = "TOKEN";
1779
2180
  const scaled = $07b3982e2fc4c8b2$var$xrplIssuedValueToAmount(deliveredOrAmount.value);
1780
2181
  parsedAmount = scaled.amount;
@@ -1790,7 +2191,7 @@ const $07b3982e2fc4c8b2$export$400f08bfae9ee97f = {
1790
2191
  memo: typedTx.DestinationTag,
1791
2192
  timestamp: typedBlock.close_time_iso || null,
1792
2193
  to: typedTx.Destination ?? "UNKNOWN",
1793
- token: tokenSymbol,
2194
+ token: issuer ? `${tokenSymbol}.${issuer}` : tokenSymbol,
1794
2195
  tokenType: tokenType,
1795
2196
  transactionGasFee: BigInt(typedTx.Fee ?? "0"),
1796
2197
  transactionHash: typedTx.hash ?? ""
@@ -1898,7 +2299,7 @@ const $07b3982e2fc4c8b2$export$400f08bfae9ee97f = {
1898
2299
  memo: undefined,
1899
2300
  timestamp: "2026-01-01T00:00:00Z",
1900
2301
  to: "rBITXTOxxxxxxxxxxxxxxxxxxxxxxxxxxx",
1901
- token: "BITx",
2302
+ token: "BITx.rBitcoiNXev8VoVxV7pwoQx1sSfonVP9i3",
1902
2303
  tokenType: "TOKEN",
1903
2304
  transactionGasFee: 12n,
1904
2305
  transactionHash: "BITXSMALLHASH"
@@ -1941,7 +2342,7 @@ const $07b3982e2fc4c8b2$export$400f08bfae9ee97f = {
1941
2342
  memo: undefined,
1942
2343
  timestamp: "2026-01-01T00:00:00Z",
1943
2344
  to: "rUSDTOxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
1944
- token: "USD",
2345
+ token: "USD.rIssuerxxxxxxxxxxxxxxxxxxxxxxxxxxx",
1945
2346
  tokenType: "TOKEN",
1946
2347
  transactionGasFee: 15n,
1947
2348
  transactionHash: "USDWHOLEHASH"
@@ -1984,7 +2385,7 @@ const $07b3982e2fc4c8b2$export$400f08bfae9ee97f = {
1984
2385
  memo: undefined,
1985
2386
  timestamp: "2026-01-01T00:00:00Z",
1986
2387
  to: "rMAGTOxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
1987
- token: "MAG",
2388
+ token: "MAG.rXmagwMmnFtVet3uL26Q2iwk287SRvVMJ",
1988
2389
  tokenType: "TOKEN",
1989
2390
  transactionGasFee: 10n,
1990
2391
  transactionHash: "MAGSCIHASH"