@clayno-club/asset-flow 0.2.2 → 0.4.0

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/index.js CHANGED
@@ -32,6 +32,7 @@ __export(solana_exports, {
32
32
  extractValueMovements: () => extractValueMovements,
33
33
  getMatchingAssetFlowMatchers: () => getMatchingAssetFlowMatchers,
34
34
  getMintNftTransfers: () => getMintNftTransfers,
35
+ hasLoanProgramSettlement: () => hasLoanProgramSettlement,
35
36
  hasMeaningfulNativeSettlement: () => hasMeaningfulNativeSettlement,
36
37
  interpretMintFlowProtocolEvent: () => interpretMintFlowProtocolEvent,
37
38
  isMintNftTransfer: () => isMintNftTransfer,
@@ -107,6 +108,15 @@ function hasMeaningfulNativeSettlement(tx, mint) {
107
108
  }
108
109
  return (tx.actions ?? []).some((action) => isMeaningfulPaidAction(action, primaryBuyer, primarySeller));
109
110
  }
111
+ function hasLoanProgramSettlement(tx) {
112
+ for (const entry of tx.accountData ?? []) {
113
+ const delta = toNullableNumber(entry.nativeBalanceChange);
114
+ if (delta !== null && Math.abs(delta) >= MEANINGFUL_SETTLEMENT_LAMPORTS) {
115
+ return true;
116
+ }
117
+ }
118
+ return false;
119
+ }
110
120
 
111
121
  // src/chains/solana/facts/build-facts.ts
112
122
  var NOISY_OWNERSHIP_CHANGE_SOURCES = /* @__PURE__ */ new Set([
@@ -157,6 +167,7 @@ function buildTransactionFacts(tx, mint) {
157
167
  const nftTouchKind = !hasTransferTouch ? "NONE" : hasOwnershipChange ? "OWNERSHIP_CHANGE" : primarySeller === primaryBuyer ? "SAME_OWNER" : "SAME_OWNER";
158
168
  const ownershipKind = !hasTransferTouch ? "NONE" : isNoisyOwnershipChange ? "AMBIGUOUS" : hasOwnershipChange ? "DIRECT" : primarySeller && primaryBuyer && primarySeller === primaryBuyer ? "SAME_OWNER" : "AMBIGUOUS";
159
169
  const settlementKind = hasMeaningfulSettlement ? primaryBuyer && primarySeller ? "NATIVE_SALE" : "POTENTIAL_SALE" : hasOwnershipChange && !hasProgramEvidence ? "AMBIGUOUS" : "NONE";
170
+ const mintInAccountData = (tx.accountData ?? []).some((entry) => entry.account === mint);
160
171
  return {
161
172
  mint,
162
173
  tx,
@@ -173,6 +184,7 @@ function buildTransactionFacts(tx, mint) {
173
184
  custodyEndpoint: hasTransferTouch ? primaryBuyer : null,
174
185
  hasProgramEvidence,
175
186
  isNoisyOwnershipChange,
187
+ mintInAccountData,
176
188
  programIds: [...programIds],
177
189
  instructionLabels: [...instructionLabels]
178
190
  };
@@ -294,6 +306,7 @@ var HADESWAP_SOURCE = "HADESWAP";
294
306
  var MAGIC_EDEN_M2_PROGRAM_ID = "M2mx93ekt1fmXSVkTrUL9xVFHkmME8HTUi5Cyc5aF7K";
295
307
  var BUY_INTENT_INSTRUCTION_LABEL = "BuyV2";
296
308
  var MEANINGFUL_SETTLEMENT_LAMPORTS2 = 1e7;
309
+ var RAW_MARKETPLACE_LISTING_LABELS = /* @__PURE__ */ new Set(["MIP1SELL", "LISTLEGACY"]);
297
310
  function isPureNonSettlementObservation(observation) {
298
311
  return observation.nftTransfers.length === 0 && !observation.hasOwnershipChange && !observation.hasMeaningfulSettlement;
299
312
  }
@@ -301,7 +314,9 @@ function isUnknownSaleLikeMarketplaceObservation(observation, source) {
301
314
  return observation.normalizedType === "UNKNOWN" && observation.normalizedSource === source && observation.hasOwnershipChange && observation.hasMeaningfulSettlement;
302
315
  }
303
316
  function isStandardMarketplaceListing(observation) {
304
- return !observation.hasMeaningfulSettlement && (MARKETPLACE_SOURCES.has(observation.normalizedSource) && LISTING_TYPES.has(observation.normalizedType) || observation.programIds.includes(TENSOR_LEGACY_MARKETPLACE_PROGRAM_ID) || observation.programIds.includes(MAGIC_EDEN_MMM_PROGRAM_ID));
317
+ return !observation.hasMeaningfulSettlement && (MARKETPLACE_SOURCES.has(observation.normalizedSource) && LISTING_TYPES.has(observation.normalizedType) || observation.normalizedSource === "RAW_WEBHOOK" && observation.instructionLabels.some(
318
+ (label) => RAW_MARKETPLACE_LISTING_LABELS.has(label.replace(/[^a-z0-9]+/gi, "").toUpperCase())
319
+ ) || observation.programIds.includes(TENSOR_LEGACY_MARKETPLACE_PROGRAM_ID) || observation.programIds.includes(MAGIC_EDEN_MMM_PROGRAM_ID));
305
320
  }
306
321
  function isFoxyRaffleListing(observation) {
307
322
  return observation.normalizedSource === FOXY_RAFFLE_SOURCE && observation.hasOwnershipChange && (observation.normalizedType === FOXY_RAFFLE_CREATE_TYPE || observation.normalizedType === FOXY_RAFFLE_UNWIND_TYPE && !observation.hasMeaningfulSettlement && !observation.programIds.includes(ASSOCIATED_TOKEN_PROGRAM_ID));
@@ -325,6 +340,20 @@ function resolveMarketplaceListing(observation) {
325
340
  hasTrackedMovement: true
326
341
  };
327
342
  }
343
+ function resolveMarketplacePassthroughTransfer(observation) {
344
+ if (!isStandardMarketplaceListing(observation) || isDelistLikeMarketplaceAction(observation)) {
345
+ return null;
346
+ }
347
+ if (observation.nftTransfers.length < 2 || !observation.hasOwnershipChange) {
348
+ return null;
349
+ }
350
+ const fromAddress = observation.primarySeller;
351
+ const toAddress = observation.primaryBuyer;
352
+ if (!fromAddress || !toAddress || fromAddress === toAddress) {
353
+ return null;
354
+ }
355
+ return { matchedBy: "LISTING_SHAPED_NET_OWNER_CHANGE", fromAddress, toAddress };
356
+ }
328
357
  function resolveMarketplaceBid(observation) {
329
358
  if (!isPureNonSettlementObservation(observation)) {
330
359
  return null;
@@ -394,6 +423,7 @@ function resolveRoutedSettlement(observation, intermediary, buyer) {
394
423
  intermediary,
395
424
  buyer,
396
425
  counterparty: to,
426
+ amount,
397
427
  matchedBy: "INTERMEDIARY_OUTBOUND_SETTLEMENT"
398
428
  };
399
429
  }
@@ -402,6 +432,7 @@ function resolveRoutedSettlement(observation, intermediary, buyer) {
402
432
  intermediary,
403
433
  buyer,
404
434
  counterparty: from,
435
+ amount,
405
436
  matchedBy: "INTERMEDIARY_INBOUND_SETTLEMENT"
406
437
  };
407
438
  }
@@ -526,6 +557,28 @@ var marketplaceListingMatcher = {
526
557
  }
527
558
  };
528
559
 
560
+ // src/chains/solana/matchers/marketplace/marketplace-passthrough-transfer.ts
561
+ var marketplacePassthroughTransferMatcher = {
562
+ id: "solana.marketplace-passthrough-transfer",
563
+ matches(observation) {
564
+ return resolveMarketplacePassthroughTransfer(observation) !== null;
565
+ },
566
+ classify(observation) {
567
+ if (!resolveMarketplacePassthroughTransfer(observation)) {
568
+ throw new Error(
569
+ `Marketplace passthrough transfer matcher classified a non-matching transaction: ${observation.tx.signature}`
570
+ );
571
+ }
572
+ return classification({
573
+ family: "GENERIC_TRANSFER",
574
+ derivedType: "TRANSFER",
575
+ matcherId: this.id,
576
+ reason: "matched_marketplace_passthrough_transfer_family",
577
+ observation
578
+ });
579
+ }
580
+ };
581
+
529
582
  // src/chains/solana/matchers/marketplace/hyperspace-instant-sell.ts
530
583
  var hyperspaceInstantSellMatcher = {
531
584
  id: "solana.hyperspace-instant-sell",
@@ -728,10 +781,14 @@ var foxyCancelSwapMatcher = {
728
781
  };
729
782
 
730
783
  // src/chains/solana/matchers/protocol/foxy-init-swap.ts
784
+ function hasRawFoxyInitSwapDeposit(labels) {
785
+ const normalized = new Set(labels.map((label) => label.replace(/[^a-z0-9]+/gi, "").toUpperCase()));
786
+ return [...normalized].some((label) => label === "INITSWAP" || label === "INITSWAP2") && normalized.has("DEPOSITPNFT");
787
+ }
731
788
  var foxyInitSwapMatcher = {
732
789
  id: "solana.foxy-init-swap",
733
790
  matches(observation) {
734
- return observation.normalizedType === "INIT_SWAP" && observation.normalizedSource === "FOXY";
791
+ return observation.normalizedType === "INIT_SWAP" && observation.normalizedSource === "FOXY" || observation.normalizedSource === "RAW_WEBHOOK" && observation.normalizedType === "NFT_TRANSFER" && hasRawFoxyInitSwapDeposit(observation.instructionLabels);
735
792
  },
736
793
  classify(observation) {
737
794
  const trackedMintMoves = observation.nftTransfers.length > 0 && observation.hasOwnershipChange;
@@ -846,8 +903,24 @@ var hadeswapActivityMatcher = {
846
903
  };
847
904
 
848
905
  // src/semantics/loan.ts
906
+ var BENEFICIAL_OWNERSHIP_DERIVED_TYPES = /* @__PURE__ */ new Set([
907
+ "MINT",
908
+ "SALE",
909
+ "TRANSFER",
910
+ "SWAP",
911
+ "FORECLOSURE"
912
+ ]);
849
913
  var LOAN_PROGRAM_SOURCES = /* @__PURE__ */ new Set(["SHARKY_FI", "CITRUS", "RAINFI", "CARDINAL_RENT", "FRAKT"]);
850
- var LOAN_LOCK_TYPES = /* @__PURE__ */ new Set(["TAKE_LOAN", "BORROW_SOL_FOR_NFT", "REBORROW_SOL_FOR_NFT", "BORROW_PERPETUAL"]);
914
+ var LOAN_LOCK_TYPES = /* @__PURE__ */ new Set([
915
+ "TAKE_LOAN",
916
+ "BORROW_SOL_FOR_NFT",
917
+ "REBORROW_SOL_FOR_NFT",
918
+ "BORROW_PERPETUAL",
919
+ "FREEZE",
920
+ // RainFi: collateral deposit
921
+ "EXTEND_LOAN"
922
+ // RainFi: loan rollover (treated as a fresh lock for chain integrity)
923
+ ]);
851
924
  var LOAN_UNLOCK_TYPES = /* @__PURE__ */ new Set(["REPAY_LOAN", "REPAY_PERPETUAL_LOAN", "REPAY", "UNFREEZE"]);
852
925
  var LOAN_UNLOCK_INSTRUCTION_LABELS = /* @__PURE__ */ new Set([
853
926
  "DelistCollateral",
@@ -896,7 +969,10 @@ function resolveLoanProgramActivity(observation) {
896
969
  if (!isLoanProgramSource(observation.normalizedSource) || !isLoanActivityType(observation.normalizedType) || observation.hasOwnershipChange && observation.hasMeaningfulSettlement) {
897
970
  return null;
898
971
  }
899
- const hasTrackedMintTouch = observation.nftTransfers.length > 0;
972
+ const hasExplicitLockType = isLoanLockType(observation.normalizedType) || isLoanUnlockType(observation.normalizedType);
973
+ const baseDelegationGate = observation.nftTransfers.length === 0 && observation.mintInAccountData;
974
+ const isDelegationLifecycle = baseDelegationGate && (hasExplicitLockType || observation.normalizedType === "UNKNOWN" && hasLoanProgramSettlement(observation.tx));
975
+ const hasTrackedMintTouch = observation.nftTransfers.length > 0 || isDelegationLifecycle;
900
976
  const lifecycleDirection = inferLoanLifecycleDirection(observation);
901
977
  return {
902
978
  lifecycleDirection,
@@ -944,6 +1020,31 @@ function resolveLoanCollateralTransfer(observation) {
944
1020
  }
945
1021
  return { derivedType: "TRANSFER" };
946
1022
  }
1023
+ function repairForeclosureFromAddresses(flows) {
1024
+ const result = [];
1025
+ let currentHolder = null;
1026
+ const activeEscrows = /* @__PURE__ */ new Set();
1027
+ for (const flow of flows) {
1028
+ const repaired = flow.derivedType === "FORECLOSURE" && currentHolder !== null && flow.fromAddress !== currentHolder ? { ...flow, fromAddress: currentHolder } : flow;
1029
+ result.push(repaired);
1030
+ if (repaired.derivedType === "LIST" || repaired.derivedType === "LOCK") {
1031
+ if (repaired.toAddress) activeEscrows.add(repaired.toAddress);
1032
+ continue;
1033
+ }
1034
+ if (repaired.derivedType === "DELIST" || repaired.derivedType === "UNLOCK") {
1035
+ if (repaired.fromAddress) activeEscrows.delete(repaired.fromAddress);
1036
+ if (repaired.toAddress && repaired.toAddress !== currentHolder && !activeEscrows.has(repaired.toAddress)) {
1037
+ currentHolder = repaired.toAddress;
1038
+ }
1039
+ continue;
1040
+ }
1041
+ if (BENEFICIAL_OWNERSHIP_DERIVED_TYPES.has(repaired.derivedType)) {
1042
+ if (repaired.fromAddress) activeEscrows.delete(repaired.fromAddress);
1043
+ currentHolder = repaired.toAddress;
1044
+ }
1045
+ }
1046
+ return result;
1047
+ }
947
1048
 
948
1049
  // src/chains/solana/matchers/protocol/loan-collateral-transfer.ts
949
1050
  var loanCollateralTransferMatcher = {
@@ -1054,12 +1155,15 @@ var metaplexMetadataUpdateMatcher = {
1054
1155
  }
1055
1156
  };
1056
1157
 
1057
- // src/chains/solana/matchers/protocol/sharky-repay-loan-sale.ts
1058
- var SHARKY_REPAY_TYPES = /* @__PURE__ */ new Set(["REPAY_LOAN", "UNKNOWN"]);
1059
- var sharkyRepayLoanSaleMatcher = {
1060
- id: "solana.sharky-repay-loan-sale",
1158
+ // src/chains/solana/matchers/protocol/loan-repay-with-settlement.ts
1159
+ var loanRepayWithSettlementMatcher = {
1160
+ id: "solana.loan-repay-with-settlement",
1061
1161
  matches(observation) {
1062
- return observation.normalizedSource === "SHARKY_FI" && SHARKY_REPAY_TYPES.has(observation.normalizedType) && observation.hasOwnershipChange && observation.hasMeaningfulSettlement && observation.nftTransfers.length > 0;
1162
+ if (!isLoanProgramSource(observation.normalizedSource)) return false;
1163
+ if (!observation.hasOwnershipChange || !observation.hasMeaningfulSettlement) return false;
1164
+ if (observation.nftTransfers.length === 0) return false;
1165
+ if (isLoanUnlockType(observation.normalizedType)) return true;
1166
+ return observation.normalizedSource === "SHARKY_FI" && observation.normalizedType === "UNKNOWN";
1063
1167
  },
1064
1168
  classify(observation) {
1065
1169
  return classification({
@@ -1069,7 +1173,6 @@ var sharkyRepayLoanSaleMatcher = {
1069
1173
  reason: "matched_loan_collateral_transfer_family",
1070
1174
  observation,
1071
1175
  overrides: {
1072
- normalizedType: "REPAY_LOAN",
1073
1176
  effect: "OWNERSHIP_CHANGE_AND_SETTLEMENT",
1074
1177
  shouldExtractValueMovements: true
1075
1178
  }
@@ -1079,19 +1182,19 @@ var sharkyRepayLoanSaleMatcher = {
1079
1182
 
1080
1183
  // src/chains/solana/matchers/protocol/sharky-take-loan-transfer.ts
1081
1184
  var sharkyTakeLoanTransferMatcher = {
1082
- id: "solana.sharky-take-loan-transfer",
1185
+ id: "solana.loan-take-with-transfer",
1083
1186
  matches(observation) {
1084
- return observation.normalizedSource === "SHARKY_FI" && observation.normalizedType === "TAKE_LOAN" && observation.hasOwnershipChange && observation.hasMeaningfulSettlement && observation.nftTransfers.length > 0;
1187
+ return isLoanProgramSource(observation.normalizedSource) && isLoanLockType(observation.normalizedType) && observation.hasOwnershipChange && observation.hasMeaningfulSettlement && observation.nftTransfers.length > 0;
1085
1188
  },
1086
1189
  classify(observation) {
1087
1190
  return classification({
1088
1191
  family: "LOAN_PROGRAM_ACTIVITY",
1089
1192
  derivedType: "TRANSFER",
1090
1193
  matcherId: this.id,
1091
- reason: "sharky_take_loan_with_settlement",
1194
+ reason: "loan_take_with_settlement",
1092
1195
  observation,
1093
1196
  overrides: {
1094
- normalizedType: "TAKE_LOAN",
1197
+ normalizedType: observation.normalizedType,
1095
1198
  effect: "OWNERSHIP_CHANGE_AND_SETTLEMENT",
1096
1199
  shouldExtractValueMovements: false
1097
1200
  }
@@ -1271,7 +1374,7 @@ var protocolMatchers = registerMatchers([
1271
1374
  [loanProgramActivityMatcher, 870],
1272
1375
  [migrateToPnftMatcher, 860],
1273
1376
  [sharkyTakeLoanTransferMatcher, 855],
1274
- [sharkyRepayLoanSaleMatcher, 850],
1377
+ [loanRepayWithSettlementMatcher, 850],
1275
1378
  [metaplexResizeActivityMatcher, 840],
1276
1379
  [metaplexMetadataUpdateMatcher, 830]
1277
1380
  ]);
@@ -1282,6 +1385,7 @@ var coreAndMarketplaceMatchers = registerMatchers([
1282
1385
  [magicEdenLuckyBuyMatcher, 670],
1283
1386
  [hyperspaceInstantSellMatcher, 665],
1284
1387
  [tensorUnknownSaleMatcher, 660],
1388
+ [marketplacePassthroughTransferMatcher, 655],
1285
1389
  [marketplaceListingMatcher, 650],
1286
1390
  [magicEdenBuyIntentMatcher, 640],
1287
1391
  [marketplaceBidMatcher, 630]
@@ -1325,6 +1429,7 @@ function observationFromFacts(facts) {
1325
1429
  hasMeaningfulSettlement: facts.hasMeaningfulSettlement,
1326
1430
  primaryBuyer: facts.primaryBuyer,
1327
1431
  primarySeller: facts.primarySeller,
1432
+ mintInAccountData: facts.mintInAccountData,
1328
1433
  programIds: facts.programIds,
1329
1434
  instructionLabels: facts.instructionLabels
1330
1435
  };
@@ -1771,7 +1876,27 @@ function interpretMintFlowProtocolEvent(params) {
1771
1876
  const derivedType = deriveMintFlowType(classification2, normalizedType);
1772
1877
  if (normalizedType !== "NFT_MINT" && derivedType === "UNKNOWN") return null;
1773
1878
  const mintTransfers = getMintTransfers(tx, mint);
1774
- if (mintTransfers.length === 0) return null;
1879
+ if (mintTransfers.length === 0) {
1880
+ if (classification2 && classification2.family === "LOAN_PROGRAM_ACTIVITY" && (derivedType === "LOCK" || derivedType === "UNLOCK") && tx.feePayer) {
1881
+ const borrower = tx.feePayer;
1882
+ return {
1883
+ kind: "DIRECT_FLOW",
1884
+ tx,
1885
+ timestamp,
1886
+ classification: classification2,
1887
+ flows: [
1888
+ {
1889
+ fromAddress: borrower,
1890
+ toAddress: borrower,
1891
+ transferIndex: 0,
1892
+ tokenStandard: null,
1893
+ derivedType
1894
+ }
1895
+ ]
1896
+ };
1897
+ }
1898
+ return null;
1899
+ }
1775
1900
  const { firstTransfer, lastTransfer, fromAddress, toAddress, tokenStandard, intermediaries } = getTransferEndpoints(mintTransfers);
1776
1901
  if (derivedType !== "MINT" && derivedType !== "LOCK" && derivedType !== "UNLOCK" && fromAddress === toAddress) {
1777
1902
  return null;
@@ -1805,6 +1930,31 @@ function interpretMintFlowProtocolEvent(params) {
1805
1930
  }
1806
1931
  }
1807
1932
  }
1933
+ const isCitrusComboBuy = (derivedType === "UNLOCK" || derivedType === "SALE") && normalizeEnhancedType(tx.source) === "CITRUS" && fromAddress !== null && toAddress !== null && fromAddress !== toAddress;
1934
+ if (isCitrusComboBuy && fromAddress && toAddress) {
1935
+ return {
1936
+ kind: "DIRECT_FLOW",
1937
+ tx,
1938
+ timestamp,
1939
+ classification: classification2,
1940
+ flows: [
1941
+ {
1942
+ fromAddress,
1943
+ toAddress,
1944
+ transferIndex: 0,
1945
+ tokenStandard,
1946
+ derivedType
1947
+ },
1948
+ {
1949
+ fromAddress: toAddress,
1950
+ toAddress,
1951
+ transferIndex: 1,
1952
+ tokenStandard,
1953
+ derivedType: "LOCK"
1954
+ }
1955
+ ]
1956
+ };
1957
+ }
1808
1958
  if (derivedType === "SALE") {
1809
1959
  const resolution = resolveSaleProtocolSemantics({
1810
1960
  tx,
@@ -2193,6 +2343,13 @@ function extractCandidateMintsFromEnhancedTx(tx) {
2193
2343
  mints.add(action.mint);
2194
2344
  }
2195
2345
  }
2346
+ for (const account of tx.accountData ?? []) {
2347
+ for (const change of account.tokenBalanceChanges ?? []) {
2348
+ if (change.mint) {
2349
+ mints.add(change.mint);
2350
+ }
2351
+ }
2352
+ }
2196
2353
  return [...mints];
2197
2354
  }
2198
2355
  function buildFlow(tx, timestamp, params) {
@@ -2258,7 +2415,9 @@ function extractValueMovements(transactions, mint, classificationCache = /* @__P
2258
2415
  }
2259
2416
  function normalizeMintHistory(transactions, mint) {
2260
2417
  const classificationCache = /* @__PURE__ */ new Map();
2261
- const flows = collapseRedundantCustodyFlows(extractMintFlows(transactions, mint, classificationCache));
2418
+ const flows = repairForeclosureFromAddresses(
2419
+ collapseRedundantCustodyFlows(extractMintFlows(transactions, mint, classificationCache))
2420
+ );
2262
2421
  const valueMovements = extractValueMovements(transactions, mint, classificationCache);
2263
2422
  const periods = reconstructOwnershipPeriods(flows);
2264
2423
  return {
@@ -2648,6 +2807,8 @@ function shouldFetchRawForMintTx(tx, mint) {
2648
2807
  var sui_exports = {};
2649
2808
  __export(sui_exports, {
2650
2809
  KIOSK_BIDDING_CLAIM_EVENT_SUFFIX: () => KIOSK_BIDDING_CLAIM_EVENT_SUFFIX,
2810
+ KIOSK_BIDDING_MATCH_BID_EVENT_SUFFIX: () => KIOSK_BIDDING_MATCH_BID_EVENT_SUFFIX,
2811
+ KIOSK_BIDDING_MATCH_COLLECTION_BID_EVENT_SUFFIX: () => KIOSK_BIDDING_MATCH_COLLECTION_BID_EVENT_SUFFIX,
2651
2812
  KIOSK_CLAIM_EVENT_SUFFIX: () => KIOSK_CLAIM_EVENT_SUFFIX,
2652
2813
  KIOSK_LIST_EVENT_SUFFIX: () => KIOSK_LIST_EVENT_SUFFIX,
2653
2814
  KIOSK_TRANSFER_EVENT_SUFFIX: () => KIOSK_TRANSFER_EVENT_SUFFIX,
@@ -2660,6 +2821,7 @@ __export(sui_exports, {
2660
2821
  TRADEPORT_ADD_LISTING_EVENT_SUFFIX: () => TRADEPORT_ADD_LISTING_EVENT_SUFFIX,
2661
2822
  TRADEPORT_BUY_SIMPLE_EVENT_SUFFIX: () => TRADEPORT_BUY_SIMPLE_EVENT_SUFFIX,
2662
2823
  TRADEPORT_CANCEL_SIMPLE_EVENT_SUFFIX: () => TRADEPORT_CANCEL_SIMPLE_EVENT_SUFFIX,
2824
+ TRADEPORT_CREATE_SIMPLE_LISTING_EVENT_SUFFIX: () => TRADEPORT_CREATE_SIMPLE_LISTING_EVENT_SUFFIX,
2663
2825
  TRADEPORT_CREATE_SINGLE_BID_EVENT_SUFFIX: () => TRADEPORT_CREATE_SINGLE_BID_EVENT_SUFFIX,
2664
2826
  TRADEPORT_KIOSK_BUY_EVENT_SUFFIX: () => TRADEPORT_KIOSK_BUY_EVENT_SUFFIX,
2665
2827
  TRADEPORT_MATCH_SINGLE_BID_EVENT_SUFFIX: () => TRADEPORT_MATCH_SINGLE_BID_EVENT_SUFFIX,
@@ -2668,17 +2830,20 @@ __export(sui_exports, {
2668
2830
  buildAssetHistory: () => normalizeAssetHistory,
2669
2831
  buildFlows: () => extractAssetFlows,
2670
2832
  buildOwnershipPeriods: () => reconstructOwnershipPeriods,
2833
+ buildValueMovements: () => extractSuiValueMovements,
2671
2834
  classify: () => classifySuiTransactionForAsset,
2672
2835
  classifySuiTransactionForAsset: () => classifySuiTransactionForAsset,
2673
2836
  eventTouchesAsset: () => eventTouchesAsset,
2674
2837
  eventTypeMatches: () => eventTypeMatches,
2675
2838
  extractAssetFlows: () => extractAssetFlows,
2839
+ extractSuiValueMovements: () => extractSuiValueMovements,
2676
2840
  findFirstEvent: () => findFirstEvent,
2677
2841
  getAssetEvents: () => getAssetEvents,
2678
2842
  getAssetObjectChange: () => getAssetObjectChange,
2679
2843
  getAssetOwnerChange: () => getAssetOwnerChange,
2680
2844
  getAssetOwnerChangeDetails: () => getAssetOwnerChangeDetails,
2681
2845
  getEventTypes: () => getEventTypes,
2846
+ getKioskOwnerCapAddressOwnerChange: () => getKioskOwnerCapAddressOwnerChange,
2682
2847
  getMarket: () => getMarket,
2683
2848
  getPriceRaw: () => getPriceRaw,
2684
2849
  getRecordString: () => getRecordString,
@@ -2700,10 +2865,13 @@ var TRADEPORT_BUY_SIMPLE_EVENT_SUFFIX = "::tradeport_listings::BuySimpleListingE
2700
2865
  var TRADEPORT_MATCH_SINGLE_BID_EVENT_SUFFIX = "::tradeport_biddings::MatchSingleBidEvent";
2701
2866
  var TRADEPORT_KIOSK_BUY_EVENT_SUFFIX = "::kiosk_listings::BuyEvent";
2702
2867
  var TRADEPORT_ADD_LISTING_EVENT_SUFFIX = "::tradeport_orderbook::AddListingEvent";
2868
+ var TRADEPORT_CREATE_SIMPLE_LISTING_EVENT_SUFFIX = "::tradeport_listings::CreateSimpleListingEvent";
2703
2869
  var TRADEPORT_RELIST_SIMPLE_EVENT_SUFFIX = "::tradeport_listings::RelistSimpleListingEvent";
2704
2870
  var TRADEPORT_REMOVE_LISTING_EVENT_SUFFIX = "::tradeport_orderbook::RemoveListingEvent";
2705
2871
  var TRADEPORT_CANCEL_SIMPLE_EVENT_SUFFIX = "::tradeport_listings::CancelSimpleListingEvent";
2706
2872
  var TRADEPORT_CREATE_SINGLE_BID_EVENT_SUFFIX = "::tradeport_biddings::CreateSingleBidEvent";
2873
+ var KIOSK_BIDDING_MATCH_BID_EVENT_SUFFIX = "::kiosk_biddings::MatchBidWithPurchaseCapEvent";
2874
+ var KIOSK_BIDDING_MATCH_COLLECTION_BID_EVENT_SUFFIX = "::kiosk_biddings::MatchCollectionBidWithPurchaseCapEvent";
2707
2875
  var KIOSK_LIST_EVENT_SUFFIX = "::kiosk_listings::ListEvent";
2708
2876
  var KIOSK_UNLIST_EVENT_SUFFIX = "::kiosk_listings::UnlistEvent";
2709
2877
  var KIOSK_TRANSFER_EVENT_SUFFIX = "::kiosk_transfers::TransferWithPurchaseCapEvent";
@@ -2778,6 +2946,31 @@ function getAssetOwnerChange(tx, assetId) {
2778
2946
  objectType: ownerChange.objectType
2779
2947
  };
2780
2948
  }
2949
+ function getKioskOwnerCapAddressOwnerChange(tx) {
2950
+ const ownerCapChanges = (tx.objectChanges ?? []).filter(
2951
+ (change) => getString(change.objectType)?.endsWith("::kiosk::KioskOwnerCap") && getString(change.type) !== "created"
2952
+ );
2953
+ const addressOwners = [
2954
+ ...new Set(
2955
+ ownerCapChanges.flatMap((change) => {
2956
+ const owner = normalizeOwnerRef(change.owner ?? change.recipient);
2957
+ return owner?.kind === "address" ? [owner.value] : [];
2958
+ })
2959
+ )
2960
+ ];
2961
+ if (addressOwners.length !== 1) {
2962
+ return null;
2963
+ }
2964
+ const sender = getString(ownerCapChanges[0]?.sender) ?? getString(tx.transaction?.data?.sender);
2965
+ if (sender === addressOwners[0]) {
2966
+ return null;
2967
+ }
2968
+ return {
2969
+ sender,
2970
+ nextOwner: { value: addressOwners[0], kind: "address" },
2971
+ objectType: getString(ownerCapChanges[0]?.objectType)
2972
+ };
2973
+ }
2781
2974
  function eventTouchesAsset(event, assetId) {
2782
2975
  if (!isRecord(event.parsedJson)) {
2783
2976
  return false;
@@ -2803,11 +2996,28 @@ function findFirstEvent(events, suffixes) {
2803
2996
  }
2804
2997
  return null;
2805
2998
  }
2999
+ function parseNonNegativeBigInt(raw) {
3000
+ if (!raw) return null;
3001
+ try {
3002
+ const value = BigInt(raw);
3003
+ return value >= 0n ? value : null;
3004
+ } catch {
3005
+ return null;
3006
+ }
3007
+ }
2806
3008
  function getPriceRaw(event) {
2807
3009
  if (!event || !isRecord(event.parsedJson)) {
2808
3010
  return null;
2809
3011
  }
2810
- return getRecordString(event.parsedJson, "price") ?? getRecordString(event.parsedJson, "new_price");
3012
+ const rawPrice = getRecordString(event.parsedJson, "price") ?? getRecordString(event.parsedJson, "new_price");
3013
+ if (rawPrice && eventTypeMatches(event, [KIOSK_LIST_EVENT_SUFFIX])) {
3014
+ const price = parseNonNegativeBigInt(rawPrice);
3015
+ const commission = parseNonNegativeBigInt(getRecordString(event.parsedJson, "commission"));
3016
+ if (price !== null && commission !== null && commission <= price) {
3017
+ return (price - commission).toString();
3018
+ }
3019
+ }
3020
+ return rawPrice;
2811
3021
  }
2812
3022
  function getMarket(event, fallback = null) {
2813
3023
  if (!event || !isRecord(event.parsedJson)) {
@@ -2859,6 +3069,8 @@ function classifySuiTransactionForAsset(tx, assetId) {
2859
3069
  const saleEvent = findFirstEvent(assetEvents, [
2860
3070
  TRADEPORT_BUY_SIMPLE_EVENT_SUFFIX,
2861
3071
  TRADEPORT_MATCH_SINGLE_BID_EVENT_SUFFIX,
3072
+ KIOSK_BIDDING_MATCH_BID_EVENT_SUFFIX,
3073
+ KIOSK_BIDDING_MATCH_COLLECTION_BID_EVENT_SUFFIX,
2862
3074
  TRADEPORT_KIOSK_BUY_EVENT_SUFFIX
2863
3075
  ]);
2864
3076
  if (saleEvent) {
@@ -2946,7 +3158,7 @@ function classifySuiTransactionForAsset(tx, assetId) {
2946
3158
  assetId,
2947
3159
  family: "TRADEPORT_LISTING",
2948
3160
  effect: "NON_SETTLEMENT_MARKETPLACE_ACTION",
2949
- materialization: "SKIP_FLOW",
3161
+ materialization: "PERSIST_FLOW",
2950
3162
  confidence: "high",
2951
3163
  derivedType: "LIST",
2952
3164
  matcherId: "sui.tradeport-relist",
@@ -2960,7 +3172,10 @@ function classifySuiTransactionForAsset(tx, assetId) {
2960
3172
  ownerChange
2961
3173
  };
2962
3174
  }
2963
- const listingEvent = findFirstEvent(assetEvents, [TRADEPORT_ADD_LISTING_EVENT_SUFFIX]);
3175
+ const listingEvent = findFirstEvent(assetEvents, [
3176
+ TRADEPORT_ADD_LISTING_EVENT_SUFFIX,
3177
+ TRADEPORT_CREATE_SIMPLE_LISTING_EVENT_SUFFIX
3178
+ ]);
2964
3179
  if (listingEvent) {
2965
3180
  return {
2966
3181
  assetId,
@@ -3010,7 +3225,7 @@ function classifySuiTransactionForAsset(tx, assetId) {
3010
3225
  assetId,
3011
3226
  family: "KIOSK_LISTING",
3012
3227
  effect: "NON_SETTLEMENT_MARKETPLACE_ACTION",
3013
- materialization: "SKIP_FLOW",
3228
+ materialization: "PERSIST_FLOW",
3014
3229
  confidence: "high",
3015
3230
  derivedType: "LIST",
3016
3231
  matcherId: "sui.kiosk-price-change",
@@ -3143,6 +3358,31 @@ function classifySuiTransactionForAsset(tx, assetId) {
3143
3358
  ownerChange
3144
3359
  };
3145
3360
  }
3361
+ const kioskOwnerCapChange = getKioskOwnerCapAddressOwnerChange(tx);
3362
+ if ((tx.events?.length ?? 0) === 0 && kioskOwnerCapChange?.nextOwner?.kind === "address") {
3363
+ return {
3364
+ assetId,
3365
+ family: "GENERIC_OBJECT_TRANSFER",
3366
+ effect: "OWNERSHIP_CHANGE_ONLY",
3367
+ materialization: "PERSIST_FLOW",
3368
+ confidence: "medium",
3369
+ derivedType: "TRANSFER",
3370
+ matcherId: "sui.kiosk-owner-cap-transfer",
3371
+ reason: "matched_kiosk_owner_cap_address_transfer",
3372
+ hasOwnershipChange: true,
3373
+ shouldExtractValueMovements: false,
3374
+ eventTypes,
3375
+ market: null,
3376
+ priceRaw: null,
3377
+ currency: null,
3378
+ ownerChange: {
3379
+ sender: kioskOwnerCapChange.sender,
3380
+ nextOwner: kioskOwnerCapChange.nextOwner.value,
3381
+ nextOwnerKind: kioskOwnerCapChange.nextOwner.kind,
3382
+ objectType: kioskOwnerCapChange.objectType
3383
+ }
3384
+ };
3385
+ }
3146
3386
  return buildClassification(tx, assetId, {
3147
3387
  family: "UNKNOWN",
3148
3388
  effect: "UNKNOWN",
@@ -3160,6 +3400,7 @@ function classifySuiTransactionForAsset(tx, assetId) {
3160
3400
  }
3161
3401
 
3162
3402
  // src/chains/sui/normalize.ts
3403
+ var MIST_PER_SUI = 1e9;
3163
3404
  function parseSaleParticipants(saleEvent) {
3164
3405
  if (!saleEvent || typeof saleEvent.parsedJson !== "object" || saleEvent.parsedJson === null) {
3165
3406
  return {
@@ -3326,6 +3567,115 @@ function buildFlow2(tx, timestamp, params) {
3326
3567
  derivedType: params.derivedType
3327
3568
  };
3328
3569
  }
3570
+ function parsePositiveRawAmount(value) {
3571
+ const raw = typeof value === "string" ? value : null;
3572
+ if (!raw) {
3573
+ return null;
3574
+ }
3575
+ try {
3576
+ return BigInt(raw) > 0n ? raw : null;
3577
+ } catch {
3578
+ return null;
3579
+ }
3580
+ }
3581
+ function toSuiAmount(rawAmount) {
3582
+ try {
3583
+ const amount = Number(rawAmount);
3584
+ if (!Number.isFinite(amount) || amount <= 0) {
3585
+ return null;
3586
+ }
3587
+ return amount / MIST_PER_SUI;
3588
+ } catch {
3589
+ return null;
3590
+ }
3591
+ }
3592
+ function buildValueMovement(tx, timestamp, params) {
3593
+ const amount = toSuiAmount(params.rawAmount);
3594
+ if (!amount) {
3595
+ return null;
3596
+ }
3597
+ return {
3598
+ signature: tx.digest,
3599
+ slot: resolveSlot(tx),
3600
+ timestamp,
3601
+ heliusType: "SUI",
3602
+ source: "sui",
3603
+ movementKind: "NATIVE",
3604
+ currency: "SUI",
3605
+ amount,
3606
+ rawAmount: params.rawAmount,
3607
+ decimals: 9,
3608
+ fromAddress: params.fromAddress,
3609
+ toAddress: params.toAddress,
3610
+ movementIndex: params.movementIndex,
3611
+ tokenStandard: null,
3612
+ role: params.role,
3613
+ confidence: "HIGH",
3614
+ isSynthetic: true
3615
+ };
3616
+ }
3617
+ function buildSaleValueMovements(tx, assetId, timestamp) {
3618
+ const saleEvent = findFirstEvent(getAssetEvents(tx, assetId), [
3619
+ TRADEPORT_BUY_SIMPLE_EVENT_SUFFIX,
3620
+ TRADEPORT_MATCH_SINGLE_BID_EVENT_SUFFIX,
3621
+ KIOSK_BIDDING_MATCH_BID_EVENT_SUFFIX,
3622
+ KIOSK_BIDDING_MATCH_COLLECTION_BID_EVENT_SUFFIX,
3623
+ TRADEPORT_KIOSK_BUY_EVENT_SUFFIX
3624
+ ]);
3625
+ if (!saleEvent || !isRecord(saleEvent.parsedJson)) {
3626
+ return [];
3627
+ }
3628
+ const parsedJson = saleEvent.parsedJson;
3629
+ const rawAmount = parsePositiveRawAmount(parsedJson.price);
3630
+ if (!rawAmount) {
3631
+ return [];
3632
+ }
3633
+ const movement = buildValueMovement(tx, timestamp, {
3634
+ movementIndex: 0,
3635
+ rawAmount,
3636
+ fromAddress: getString(parsedJson.buyer),
3637
+ toAddress: getString(parsedJson.seller),
3638
+ role: "SALE_GROSS"
3639
+ });
3640
+ return movement ? [movement] : [];
3641
+ }
3642
+ function buildSwapValueMovements(tx, assetId, timestamp) {
3643
+ const swapEvent = getAssetEvents(tx, assetId).find((event) => event.type.includes("SwapExecuted")) ?? null;
3644
+ if (!swapEvent || !isRecord(swapEvent.parsedJson)) {
3645
+ return [];
3646
+ }
3647
+ const parsedJson = swapEvent.parsedJson;
3648
+ const proposer = getString(parsedJson.proposer);
3649
+ const counterparty = getString(parsedJson.counterparty);
3650
+ const movements = [];
3651
+ const proposerRawAmount = parsePositiveRawAmount(parsedJson.proposer_sui_amount);
3652
+ const counterpartyRawAmount = parsePositiveRawAmount(parsedJson.counterparty_sui_amount);
3653
+ if (proposerRawAmount) {
3654
+ const movement = buildValueMovement(tx, timestamp, {
3655
+ movementIndex: movements.length,
3656
+ rawAmount: proposerRawAmount,
3657
+ fromAddress: proposer,
3658
+ toAddress: counterparty,
3659
+ role: "SWAP_CONSIDERATION"
3660
+ });
3661
+ if (movement) {
3662
+ movements.push(movement);
3663
+ }
3664
+ }
3665
+ if (counterpartyRawAmount) {
3666
+ const movement = buildValueMovement(tx, timestamp, {
3667
+ movementIndex: movements.length,
3668
+ rawAmount: counterpartyRawAmount,
3669
+ fromAddress: counterparty,
3670
+ toAddress: proposer,
3671
+ role: "SWAP_CONSIDERATION"
3672
+ });
3673
+ if (movement) {
3674
+ movements.push(movement);
3675
+ }
3676
+ }
3677
+ return movements;
3678
+ }
3329
3679
  function buildSuiFlows(tx, assetId, classification2, currentBeneficialOwner) {
3330
3680
  if (classification2.materialization !== "PERSIST_FLOW") {
3331
3681
  return [];
@@ -3333,8 +3683,10 @@ function buildSuiFlows(tx, assetId, classification2, currentBeneficialOwner) {
3333
3683
  const timestamp = resolveTimestamp(tx);
3334
3684
  const assetEvents = getAssetEvents(tx, assetId);
3335
3685
  const ownerChange = getAssetOwnerChangeDetails(tx, assetId);
3686
+ const kioskOwnerCapChange = getKioskOwnerCapAddressOwnerChange(tx);
3336
3687
  const sender = getString(tx.transaction?.data?.sender) ?? ownerChange?.sender ?? null;
3337
3688
  const addressOwner = (ownerChange?.nextOwner?.kind === "address" ? ownerChange.nextOwner.value : null) ?? resolveNextBeneficialOwner(tx, ownerChange);
3689
+ const kioskOwnerCapAddressOwner = kioskOwnerCapChange?.nextOwner?.kind === "address" ? kioskOwnerCapChange.nextOwner.value : null;
3338
3690
  let fromAddress = null;
3339
3691
  let toAddress = null;
3340
3692
  switch (classification2.derivedType) {
@@ -3345,6 +3697,8 @@ function buildSuiFlows(tx, assetId, classification2, currentBeneficialOwner) {
3345
3697
  const saleEvent = findFirstEvent(assetEvents, [
3346
3698
  TRADEPORT_BUY_SIMPLE_EVENT_SUFFIX,
3347
3699
  TRADEPORT_MATCH_SINGLE_BID_EVENT_SUFFIX,
3700
+ KIOSK_BIDDING_MATCH_BID_EVENT_SUFFIX,
3701
+ KIOSK_BIDDING_MATCH_COLLECTION_BID_EVENT_SUFFIX,
3348
3702
  TRADEPORT_KIOSK_BUY_EVENT_SUFFIX
3349
3703
  ]);
3350
3704
  const sale = parseSaleParticipants(saleEvent);
@@ -3361,10 +3715,15 @@ function buildSuiFlows(tx, assetId, classification2, currentBeneficialOwner) {
3361
3715
  }
3362
3716
  case "TRANSFER":
3363
3717
  fromAddress = currentBeneficialOwner ?? sender ?? null;
3364
- toAddress = addressOwner ?? (ownerChange?.nextOwner?.kind === "object" ? sender ?? currentBeneficialOwner ?? null : sender ?? null);
3718
+ toAddress = (classification2.matcherId === "sui.kiosk-owner-cap-transfer" ? kioskOwnerCapAddressOwner : null) ?? addressOwner ?? (ownerChange?.nextOwner?.kind === "object" ? sender ?? currentBeneficialOwner ?? null : sender ?? null);
3365
3719
  break;
3366
3720
  case "LIST": {
3367
- const listingEvent = findFirstEvent(assetEvents, [TRADEPORT_ADD_LISTING_EVENT_SUFFIX, KIOSK_LIST_EVENT_SUFFIX]);
3721
+ const listingEvent = findFirstEvent(assetEvents, [
3722
+ TRADEPORT_ADD_LISTING_EVENT_SUFFIX,
3723
+ TRADEPORT_CREATE_SIMPLE_LISTING_EVENT_SUFFIX,
3724
+ TRADEPORT_RELIST_SIMPLE_EVENT_SUFFIX,
3725
+ KIOSK_LIST_EVENT_SUFFIX
3726
+ ]);
3368
3727
  const owner = parseListingSeller(listingEvent) ?? currentBeneficialOwner ?? sender ?? addressOwner;
3369
3728
  fromAddress = owner;
3370
3729
  toAddress = owner;
@@ -3420,17 +3779,40 @@ function extractAssetFlows(transactions, assetId, classificationCache = /* @__PU
3420
3779
  }
3421
3780
  return collapseRedundantCustodyFlows(canonicalizeMintFlows(flows));
3422
3781
  }
3782
+ function extractSuiValueMovements(transactions, assetId, classificationCache = /* @__PURE__ */ new Map()) {
3783
+ const movements = [];
3784
+ for (const tx of sortTransactions(transactions)) {
3785
+ const classification2 = classificationCache.get(tx.digest) ?? classifySuiTransactionForAsset(tx, assetId);
3786
+ classificationCache.set(tx.digest, classification2);
3787
+ if (classification2.materialization !== "PERSIST_FLOW" || !classification2.shouldExtractValueMovements) {
3788
+ continue;
3789
+ }
3790
+ const timestamp = resolveTimestamp(tx);
3791
+ if (classification2.derivedType === "SALE") {
3792
+ movements.push(...buildSaleValueMovements(tx, assetId, timestamp));
3793
+ continue;
3794
+ }
3795
+ if (classification2.derivedType === "SWAP") {
3796
+ movements.push(...buildSwapValueMovements(tx, assetId, timestamp));
3797
+ }
3798
+ }
3799
+ return canonicalizeValueMovements(movements);
3800
+ }
3423
3801
  function normalizeAssetHistory(transactions, assetId) {
3424
3802
  const classificationCache = /* @__PURE__ */ new Map();
3425
3803
  const flows = extractAssetFlows(transactions, assetId, classificationCache);
3804
+ const valueMovements = extractSuiValueMovements(transactions, assetId, classificationCache);
3426
3805
  const periods = reconstructOwnershipPeriods(flows);
3427
3806
  return {
3428
3807
  flows,
3808
+ valueMovements,
3429
3809
  periods
3430
3810
  };
3431
3811
  }
3432
3812
  export {
3433
3813
  KIOSK_BIDDING_CLAIM_EVENT_SUFFIX,
3814
+ KIOSK_BIDDING_MATCH_BID_EVENT_SUFFIX,
3815
+ KIOSK_BIDDING_MATCH_COLLECTION_BID_EVENT_SUFFIX,
3434
3816
  KIOSK_CLAIM_EVENT_SUFFIX,
3435
3817
  KIOSK_LIST_EVENT_SUFFIX,
3436
3818
  KIOSK_TRANSFER_EVENT_SUFFIX,
@@ -3444,6 +3826,7 @@ export {
3444
3826
  TRADEPORT_ADD_LISTING_EVENT_SUFFIX,
3445
3827
  TRADEPORT_BUY_SIMPLE_EVENT_SUFFIX,
3446
3828
  TRADEPORT_CANCEL_SIMPLE_EVENT_SUFFIX,
3829
+ TRADEPORT_CREATE_SIMPLE_LISTING_EVENT_SUFFIX,
3447
3830
  TRADEPORT_CREATE_SINGLE_BID_EVENT_SUFFIX,
3448
3831
  TRADEPORT_KIOSK_BUY_EVENT_SUFFIX,
3449
3832
  TRADEPORT_MATCH_SINGLE_BID_EVENT_SUFFIX,
@@ -3474,6 +3857,7 @@ export {
3474
3857
  extractCandidateMintsFromRawTransaction,
3475
3858
  extractMintFlows,
3476
3859
  extractCandidateMintsFromEnhancedTx as extractMints,
3860
+ extractSuiValueMovements,
3477
3861
  extractValueMovements,
3478
3862
  findFirstEvent,
3479
3863
  getAssetEvents,
@@ -3481,6 +3865,7 @@ export {
3481
3865
  getAssetOwnerChange,
3482
3866
  getAssetOwnerChangeDetails,
3483
3867
  getEventTypes,
3868
+ getKioskOwnerCapAddressOwnerChange,
3484
3869
  getMarket,
3485
3870
  getMatchingAssetFlowMatchers,
3486
3871
  getMintNftTransfers,
@@ -3488,6 +3873,7 @@ export {
3488
3873
  getRecordString,
3489
3874
  getRecordStringArray,
3490
3875
  getString,
3876
+ hasLoanProgramSettlement,
3491
3877
  hasMeaningfulNativeSettlement,
3492
3878
  hasMeaningfulSuiSettlement,
3493
3879
  interpretMintFlowProtocolEvent,