@t2000/engine 0.54.2 → 0.56.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/README.md +29 -4
- package/dist/index.d.ts +238 -16
- package/dist/index.js +332 -112
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -532,11 +532,139 @@ function resetDefiCacheStore() {
|
|
|
532
532
|
activeStore = new InMemoryDefiCacheStore();
|
|
533
533
|
}
|
|
534
534
|
|
|
535
|
+
// src/wallet-cache.ts
|
|
536
|
+
var InMemoryWalletCacheStore = class {
|
|
537
|
+
store = /* @__PURE__ */ new Map();
|
|
538
|
+
async get(address) {
|
|
539
|
+
const slot = this.store.get(address.toLowerCase());
|
|
540
|
+
if (!slot) return null;
|
|
541
|
+
if (Date.now() >= slot.expiresAt) {
|
|
542
|
+
this.store.delete(address.toLowerCase());
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
return slot.entry;
|
|
546
|
+
}
|
|
547
|
+
async set(address, entry, ttlSec) {
|
|
548
|
+
this.store.set(address.toLowerCase(), {
|
|
549
|
+
entry,
|
|
550
|
+
expiresAt: Date.now() + ttlSec * 1e3
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
async delete(address) {
|
|
554
|
+
this.store.delete(address.toLowerCase());
|
|
555
|
+
}
|
|
556
|
+
async clear() {
|
|
557
|
+
this.store.clear();
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
var activeStore2 = new InMemoryWalletCacheStore();
|
|
561
|
+
function setWalletCacheStore(store) {
|
|
562
|
+
activeStore2 = store;
|
|
563
|
+
}
|
|
564
|
+
function getWalletCacheStore() {
|
|
565
|
+
return activeStore2;
|
|
566
|
+
}
|
|
567
|
+
function resetWalletCacheStore() {
|
|
568
|
+
activeStore2 = new InMemoryWalletCacheStore();
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// src/cross-instance-lock.ts
|
|
572
|
+
var InMemoryFetchLock = class {
|
|
573
|
+
held = /* @__PURE__ */ new Map();
|
|
574
|
+
async acquire(key, leaseSec) {
|
|
575
|
+
const now = Date.now();
|
|
576
|
+
const expiry = this.held.get(key);
|
|
577
|
+
if (expiry !== void 0 && expiry > now) return false;
|
|
578
|
+
this.held.set(key, now + leaseSec * 1e3);
|
|
579
|
+
return true;
|
|
580
|
+
}
|
|
581
|
+
async release(key) {
|
|
582
|
+
this.held.delete(key);
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
var activeLock = new InMemoryFetchLock();
|
|
586
|
+
function setFetchLock(lock) {
|
|
587
|
+
activeLock = lock;
|
|
588
|
+
}
|
|
589
|
+
function getFetchLock() {
|
|
590
|
+
return activeLock;
|
|
591
|
+
}
|
|
592
|
+
function resetFetchLock() {
|
|
593
|
+
activeLock = new InMemoryFetchLock();
|
|
594
|
+
}
|
|
595
|
+
var DEFAULT_LEASE_SEC = 15;
|
|
596
|
+
var DEFAULT_POLL_BUDGET_MS = 4500;
|
|
597
|
+
var DEFAULT_POLL_INTERVAL_MS = 100;
|
|
598
|
+
async function awaitOrFetch(key, fetcher, opts = {}) {
|
|
599
|
+
const lock = opts.lock ?? getFetchLock();
|
|
600
|
+
const leaseSec = opts.leaseSec ?? DEFAULT_LEASE_SEC;
|
|
601
|
+
const pollBudgetMs = opts.pollBudgetMs ?? DEFAULT_POLL_BUDGET_MS;
|
|
602
|
+
const pollIntervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
603
|
+
const rng = opts.rng ?? Math.random;
|
|
604
|
+
const now = opts.now ?? Date.now;
|
|
605
|
+
const sleep2 = opts.sleep ?? ((ms) => new Promise((resolve, reject) => {
|
|
606
|
+
const timer = setTimeout(resolve, ms);
|
|
607
|
+
if (opts.signal) {
|
|
608
|
+
const onAbort = () => {
|
|
609
|
+
clearTimeout(timer);
|
|
610
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
611
|
+
};
|
|
612
|
+
if (opts.signal.aborted) onAbort();
|
|
613
|
+
else opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
614
|
+
}
|
|
615
|
+
}));
|
|
616
|
+
let acquired = false;
|
|
617
|
+
try {
|
|
618
|
+
acquired = await lock.acquire(key, leaseSec);
|
|
619
|
+
} catch (err) {
|
|
620
|
+
console.warn(`[fetch-lock] acquire(${key}) threw; falling through to direct fetch:`, err);
|
|
621
|
+
return fetcher();
|
|
622
|
+
}
|
|
623
|
+
if (acquired) {
|
|
624
|
+
try {
|
|
625
|
+
return await fetcher();
|
|
626
|
+
} finally {
|
|
627
|
+
try {
|
|
628
|
+
await lock.release(key);
|
|
629
|
+
} catch (err) {
|
|
630
|
+
console.warn(`[fetch-lock] release(${key}) failed (non-fatal):`, err);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
if (!opts.pollCache) {
|
|
635
|
+
return fetcher();
|
|
636
|
+
}
|
|
637
|
+
const deadline = now() + pollBudgetMs;
|
|
638
|
+
while (now() < deadline) {
|
|
639
|
+
const jitterPx = (rng() * 0.4 - 0.2) * pollIntervalMs;
|
|
640
|
+
const wait = Math.max(0, pollIntervalMs + jitterPx);
|
|
641
|
+
try {
|
|
642
|
+
await sleep2(wait);
|
|
643
|
+
} catch (err) {
|
|
644
|
+
if (err?.name === "AbortError") throw err;
|
|
645
|
+
return fetcher();
|
|
646
|
+
}
|
|
647
|
+
let cached = null;
|
|
648
|
+
try {
|
|
649
|
+
cached = await opts.pollCache();
|
|
650
|
+
} catch (err) {
|
|
651
|
+
console.warn(`[fetch-lock] pollCache(${key}) threw; continuing to poll:`, err);
|
|
652
|
+
}
|
|
653
|
+
if (cached !== null) return cached;
|
|
654
|
+
}
|
|
655
|
+
return fetcher();
|
|
656
|
+
}
|
|
657
|
+
|
|
535
658
|
// src/blockvision-prices.ts
|
|
536
659
|
var BLOCKVISION_BASE = "https://api.blockvision.org/v2/sui";
|
|
537
660
|
var PORTFOLIO_TIMEOUT_MS = 4e3;
|
|
538
661
|
var PRICES_TIMEOUT_MS = 3e3;
|
|
539
662
|
var CACHE_TTL_MS = 6e4;
|
|
663
|
+
var WALLET_FRESH_TTL_MS_BLOCKVISION = 6e4;
|
|
664
|
+
var WALLET_FRESH_TTL_MS_DEGRADED = 15e3;
|
|
665
|
+
var WALLET_STICKY_TTL_SEC = 30 * 60;
|
|
666
|
+
var WALLET_LOCK_KEY = (address) => `bv-lock:wallet:${address.toLowerCase()}`;
|
|
667
|
+
var DEFI_LOCK_KEY = (address) => `bv-lock:defi:${address.toLowerCase()}`;
|
|
540
668
|
var BV_RETRY_MAX_ATTEMPTS = 3;
|
|
541
669
|
var BV_RETRY_BASE_DELAY_MS = 250;
|
|
542
670
|
var BV_RETRY_BACKOFF_FACTOR = 3;
|
|
@@ -617,7 +745,6 @@ async function fetchBlockVisionWithRetry(url, init, opts = {}) {
|
|
|
617
745
|
if (lastResponse) return lastResponse;
|
|
618
746
|
throw lastError ?? new Error("fetch failed after retries");
|
|
619
747
|
}
|
|
620
|
-
var DEGRADED_CACHE_TTL_MS = 15e3;
|
|
621
748
|
var PRICE_LIST_CHUNK = 10;
|
|
622
749
|
var STABLE_USD_PRICES = {
|
|
623
750
|
"0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC": 1,
|
|
@@ -627,38 +754,103 @@ var STABLE_USD_PRICES = {
|
|
|
627
754
|
"0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN": 1,
|
|
628
755
|
"0xc060006111016b8a020ad5b33834984a437aaa7d3c74c18e09a95d48aceab08c::coin::COIN": 1
|
|
629
756
|
};
|
|
630
|
-
var portfolioCache = /* @__PURE__ */ new Map();
|
|
631
757
|
var portfolioInflight = /* @__PURE__ */ new Map();
|
|
632
758
|
var priceMapCache = null;
|
|
759
|
+
function walletFreshTtlMs(source) {
|
|
760
|
+
switch (source) {
|
|
761
|
+
case "blockvision":
|
|
762
|
+
return WALLET_FRESH_TTL_MS_BLOCKVISION;
|
|
763
|
+
case "sui-rpc-degraded":
|
|
764
|
+
return WALLET_FRESH_TTL_MS_DEGRADED;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
async function safeWalletStoreSet(store, address, entry, ttlSec) {
|
|
768
|
+
try {
|
|
769
|
+
await store.set(address, entry, ttlSec);
|
|
770
|
+
} catch (err) {
|
|
771
|
+
console.warn("[wallet] cache set failed (non-fatal):", err);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
async function safeWalletStoreGet(store, address) {
|
|
775
|
+
try {
|
|
776
|
+
return await store.get(address);
|
|
777
|
+
} catch (err) {
|
|
778
|
+
console.warn("[wallet] cache get failed (continuing as cache miss):", err);
|
|
779
|
+
return null;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
633
782
|
async function fetchAddressPortfolio(address, apiKey, fallbackRpcUrl) {
|
|
634
|
-
const
|
|
635
|
-
const
|
|
636
|
-
if (
|
|
637
|
-
|
|
783
|
+
const store = getWalletCacheStore();
|
|
784
|
+
const cachedEntry = await safeWalletStoreGet(store, address);
|
|
785
|
+
if (cachedEntry) {
|
|
786
|
+
const ageMs = Date.now() - cachedEntry.pricedAt;
|
|
787
|
+
if (ageMs < walletFreshTtlMs(cachedEntry.data.source)) {
|
|
788
|
+
return cachedEntry.data;
|
|
789
|
+
}
|
|
638
790
|
}
|
|
639
|
-
|
|
640
|
-
if (
|
|
641
|
-
|
|
791
|
+
const existing = portfolioInflight.get(address);
|
|
792
|
+
if (existing) return existing;
|
|
793
|
+
const promise = (async () => {
|
|
642
794
|
try {
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
795
|
+
return await awaitOrFetch(
|
|
796
|
+
WALLET_LOCK_KEY(address),
|
|
797
|
+
// ----------------------------------------------------------
|
|
798
|
+
// Leader path — runs after we've won the cross-instance lock.
|
|
799
|
+
// Re-checks the cache (small window where another leader on a
|
|
800
|
+
// different process just wrote) before paying for the BV call.
|
|
801
|
+
// ----------------------------------------------------------
|
|
802
|
+
async () => {
|
|
803
|
+
const recheck = await safeWalletStoreGet(store, address);
|
|
804
|
+
if (recheck) {
|
|
805
|
+
const ageMs = Date.now() - recheck.pricedAt;
|
|
806
|
+
if (ageMs < walletFreshTtlMs(recheck.data.source)) {
|
|
807
|
+
return recheck.data;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
if (apiKey && apiKey.trim().length > 0) {
|
|
811
|
+
const blockvision = await fetchPortfolioFromBlockVision(address, apiKey);
|
|
812
|
+
if (blockvision) {
|
|
813
|
+
await safeWalletStoreSet(
|
|
814
|
+
store,
|
|
815
|
+
address,
|
|
816
|
+
{ data: blockvision, pricedAt: Date.now() },
|
|
817
|
+
WALLET_STICKY_TTL_SEC
|
|
818
|
+
);
|
|
819
|
+
return blockvision;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
const degraded = await fetchPortfolioFromSuiRpc(address, apiKey, fallbackRpcUrl);
|
|
823
|
+
const stickyCandidate = recheck && recheck.data.source === "blockvision" && recheck.data.totalUsd > 0 ? recheck : cachedEntry && cachedEntry.data.source === "blockvision" && cachedEntry.data.totalUsd > 0 ? cachedEntry : null;
|
|
824
|
+
const stickyFresh = stickyCandidate && Date.now() - stickyCandidate.pricedAt < WALLET_STICKY_TTL_SEC * 1e3;
|
|
825
|
+
if (stickyFresh) {
|
|
826
|
+
return stickyCandidate.data;
|
|
827
|
+
}
|
|
828
|
+
await safeWalletStoreSet(
|
|
829
|
+
store,
|
|
830
|
+
address,
|
|
831
|
+
{ data: degraded, pricedAt: Date.now() },
|
|
832
|
+
Math.ceil(WALLET_FRESH_TTL_MS_DEGRADED / 1e3)
|
|
833
|
+
);
|
|
834
|
+
return degraded;
|
|
835
|
+
},
|
|
836
|
+
{
|
|
837
|
+
// Followers poll the wallet cache while the leader fetches.
|
|
838
|
+
// Returns non-null only when the leader has written a
|
|
839
|
+
// fresh-for-source entry — stale entries keep the poll going.
|
|
840
|
+
pollCache: async () => {
|
|
841
|
+
const e = await safeWalletStoreGet(store, address);
|
|
842
|
+
if (!e) return null;
|
|
843
|
+
const ageMs = Date.now() - e.pricedAt;
|
|
844
|
+
return ageMs < walletFreshTtlMs(e.data.source) ? e.data : null;
|
|
845
|
+
}
|
|
648
846
|
}
|
|
649
|
-
|
|
650
|
-
const degraded = await fetchPortfolioFromSuiRpc(address, apiKey, fallbackRpcUrl);
|
|
651
|
-
portfolioCache.set(address, {
|
|
652
|
-
data: degraded,
|
|
653
|
-
ts: Date.now() - (CACHE_TTL_MS - DEGRADED_CACHE_TTL_MS)
|
|
654
|
-
});
|
|
655
|
-
return degraded;
|
|
847
|
+
);
|
|
656
848
|
} finally {
|
|
657
849
|
portfolioInflight.delete(address);
|
|
658
850
|
}
|
|
659
851
|
})();
|
|
660
|
-
portfolioInflight.set(address,
|
|
661
|
-
return
|
|
852
|
+
portfolioInflight.set(address, promise);
|
|
853
|
+
return promise;
|
|
662
854
|
}
|
|
663
855
|
async function fetchPortfolioFromBlockVision(address, apiKey) {
|
|
664
856
|
const url = `${BLOCKVISION_BASE}/account/coins?account=${encodeURIComponent(address)}`;
|
|
@@ -889,6 +1081,14 @@ async function safeStoreSet(store, address, entry) {
|
|
|
889
1081
|
console.warn("[defi] cache set failed (non-fatal):", err);
|
|
890
1082
|
}
|
|
891
1083
|
}
|
|
1084
|
+
async function safeDefiStoreGet(store, address) {
|
|
1085
|
+
try {
|
|
1086
|
+
return await store.get(address);
|
|
1087
|
+
} catch (err) {
|
|
1088
|
+
console.warn("[defi] cache get failed (continuing as cache miss):", err);
|
|
1089
|
+
return null;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
892
1092
|
var warnedMissingApiKey = false;
|
|
893
1093
|
async function fetchAddressDefiPortfolio(address, apiKey, priceHints = {}) {
|
|
894
1094
|
if (!apiKey || apiKey.trim().length === 0) {
|
|
@@ -901,15 +1101,9 @@ async function fetchAddressDefiPortfolio(address, apiKey, priceHints = {}) {
|
|
|
901
1101
|
return { totalUsd: 0, perProtocol: {}, pricedAt: Date.now(), source: "degraded" };
|
|
902
1102
|
}
|
|
903
1103
|
const store = getDefiCacheStore();
|
|
904
|
-
const
|
|
905
|
-
let cachedEntry = null;
|
|
906
|
-
try {
|
|
907
|
-
cachedEntry = await store.get(address);
|
|
908
|
-
} catch (err) {
|
|
909
|
-
console.warn("[defi] cache get failed (continuing as cache miss):", err);
|
|
910
|
-
}
|
|
1104
|
+
const cachedEntry = await safeDefiStoreGet(store, address);
|
|
911
1105
|
if (cachedEntry) {
|
|
912
|
-
const ageMs = now - cachedEntry.pricedAt;
|
|
1106
|
+
const ageMs = Date.now() - cachedEntry.pricedAt;
|
|
913
1107
|
const freshTtlMs = freshTtlForSource(cachedEntry.data.source);
|
|
914
1108
|
if (ageMs < freshTtlMs) {
|
|
915
1109
|
return cachedEntry.data;
|
|
@@ -919,82 +1113,108 @@ async function fetchAddressDefiPortfolio(address, apiKey, priceHints = {}) {
|
|
|
919
1113
|
if (inflight) return inflight;
|
|
920
1114
|
inflight = (async () => {
|
|
921
1115
|
try {
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1116
|
+
return await awaitOrFetch(
|
|
1117
|
+
DEFI_LOCK_KEY(address),
|
|
1118
|
+
// Leader path — runs after acquiring the lock.
|
|
1119
|
+
async () => {
|
|
1120
|
+
const recheck = await safeDefiStoreGet(store, address);
|
|
1121
|
+
if (recheck) {
|
|
1122
|
+
const ageMs = Date.now() - recheck.pricedAt;
|
|
1123
|
+
if (ageMs < freshTtlForSource(recheck.data.source)) {
|
|
1124
|
+
return recheck.data;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
const stickyBasis = recheck ?? cachedEntry;
|
|
1128
|
+
const fanoutAt = Date.now();
|
|
1129
|
+
const settled = await Promise.allSettled(
|
|
1130
|
+
DEFI_PROTOCOLS.map((p) => fetchOneDefiProtocol(address, p, apiKey))
|
|
1131
|
+
);
|
|
1132
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1133
|
+
for (const s of settled) {
|
|
1134
|
+
if (s.status === "fulfilled" && s.value) collectCoinTypes(s.value, seen);
|
|
1135
|
+
}
|
|
1136
|
+
const normalizedHints = {};
|
|
1137
|
+
for (const [k, v] of Object.entries(priceHints)) {
|
|
1138
|
+
normalizedHints[normalizeCoinType(k)] = v;
|
|
1139
|
+
}
|
|
1140
|
+
const missing = Array.from(seen).filter((ct) => {
|
|
1141
|
+
const norm = normalizeCoinType(ct);
|
|
1142
|
+
return !normalizedHints[norm] && !STABLE_USD_PRICES[norm];
|
|
1143
|
+
});
|
|
1144
|
+
let fetchedPrices = {};
|
|
1145
|
+
if (missing.length > 0) {
|
|
1146
|
+
try {
|
|
1147
|
+
fetchedPrices = await fetchTokenPrices(missing, apiKey);
|
|
1148
|
+
} catch (err) {
|
|
1149
|
+
console.warn("[defi] fill-missing-prices failed:", err);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
const prices = { ...normalizedHints };
|
|
1153
|
+
for (const [ct, v] of Object.entries(fetchedPrices)) {
|
|
1154
|
+
prices[normalizeCoinType(ct)] ??= v.price;
|
|
1155
|
+
}
|
|
1156
|
+
for (const [ct, p] of Object.entries(STABLE_USD_PRICES)) {
|
|
1157
|
+
prices[normalizeCoinType(ct)] ??= p;
|
|
1158
|
+
}
|
|
1159
|
+
let totalUsd = 0;
|
|
1160
|
+
let failures = 0;
|
|
1161
|
+
const perProtocol = {};
|
|
1162
|
+
for (let i = 0; i < DEFI_PROTOCOLS.length; i++) {
|
|
1163
|
+
const proto = DEFI_PROTOCOLS[i];
|
|
1164
|
+
const s = settled[i];
|
|
1165
|
+
if (s.status !== "fulfilled" || !s.value) {
|
|
1166
|
+
failures++;
|
|
1167
|
+
continue;
|
|
1168
|
+
}
|
|
1169
|
+
try {
|
|
1170
|
+
const usd = normalizeProtocol(proto, s.value, prices);
|
|
1171
|
+
if (Number.isFinite(usd) && usd !== 0) {
|
|
1172
|
+
perProtocol[proto] = usd;
|
|
1173
|
+
totalUsd += usd;
|
|
1174
|
+
}
|
|
1175
|
+
} catch (err) {
|
|
1176
|
+
console.warn(`[defi] ${proto} normaliser threw:`, err);
|
|
1177
|
+
failures++;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
if (totalUsd < 0) totalUsd = 0;
|
|
1181
|
+
const fetchedAt = Date.now();
|
|
1182
|
+
const summary = {
|
|
1183
|
+
totalUsd,
|
|
1184
|
+
perProtocol,
|
|
1185
|
+
pricedAt: fetchedAt,
|
|
1186
|
+
source: failures === DEFI_PROTOCOLS.length ? "degraded" : failures > 0 ? "partial" : "blockvision"
|
|
1187
|
+
};
|
|
1188
|
+
const cachedPositive = stickyBasis && stickyBasis.data.totalUsd > 0 && fanoutAt - stickyBasis.pricedAt < DEFI_STICKY_TTL_SEC * 1e3;
|
|
1189
|
+
if (summary.source === "blockvision") {
|
|
1190
|
+
await safeStoreSet(store, address, { data: summary, pricedAt: fetchedAt });
|
|
1191
|
+
return summary;
|
|
1192
|
+
}
|
|
1193
|
+
if (summary.source === "partial" && summary.totalUsd > 0) {
|
|
1194
|
+
await safeStoreSet(store, address, { data: summary, pricedAt: fetchedAt });
|
|
1195
|
+
return summary;
|
|
1196
|
+
}
|
|
1197
|
+
if (cachedPositive) {
|
|
1198
|
+
const stale = {
|
|
1199
|
+
...stickyBasis.data,
|
|
1200
|
+
source: "partial-stale"
|
|
1201
|
+
};
|
|
1202
|
+
return stale;
|
|
1203
|
+
}
|
|
1204
|
+
return summary;
|
|
1205
|
+
},
|
|
1206
|
+
{
|
|
1207
|
+
// Followers poll the DeFi cache while the leader fans out.
|
|
1208
|
+
// Returns non-null only when the leader has written a
|
|
1209
|
+
// fresh-for-source entry — stale entries keep the poll going.
|
|
1210
|
+
pollCache: async () => {
|
|
1211
|
+
const e = await safeDefiStoreGet(store, address);
|
|
1212
|
+
if (!e) return null;
|
|
1213
|
+
const ageMs = Date.now() - e.pricedAt;
|
|
1214
|
+
return ageMs < freshTtlForSource(e.data.source) ? e.data : null;
|
|
967
1215
|
}
|
|
968
|
-
} catch (err) {
|
|
969
|
-
console.warn(`[defi] ${proto} normaliser threw:`, err);
|
|
970
|
-
failures++;
|
|
971
1216
|
}
|
|
972
|
-
|
|
973
|
-
if (totalUsd < 0) totalUsd = 0;
|
|
974
|
-
const fetchedAt = Date.now();
|
|
975
|
-
const summary = {
|
|
976
|
-
totalUsd,
|
|
977
|
-
perProtocol,
|
|
978
|
-
pricedAt: fetchedAt,
|
|
979
|
-
source: failures === DEFI_PROTOCOLS.length ? "degraded" : failures > 0 ? "partial" : "blockvision"
|
|
980
|
-
};
|
|
981
|
-
const cachedPositive = cachedEntry && cachedEntry.data.totalUsd > 0 && now - cachedEntry.pricedAt < DEFI_STICKY_TTL_SEC * 1e3;
|
|
982
|
-
if (summary.source === "blockvision") {
|
|
983
|
-
await safeStoreSet(store, address, { data: summary, pricedAt: fetchedAt });
|
|
984
|
-
return summary;
|
|
985
|
-
}
|
|
986
|
-
if (summary.source === "partial" && summary.totalUsd > 0) {
|
|
987
|
-
await safeStoreSet(store, address, { data: summary, pricedAt: fetchedAt });
|
|
988
|
-
return summary;
|
|
989
|
-
}
|
|
990
|
-
if (cachedPositive) {
|
|
991
|
-
const stale = {
|
|
992
|
-
...cachedEntry.data,
|
|
993
|
-
source: "partial-stale"
|
|
994
|
-
};
|
|
995
|
-
return stale;
|
|
996
|
-
}
|
|
997
|
-
return summary;
|
|
1217
|
+
);
|
|
998
1218
|
} finally {
|
|
999
1219
|
defiInflight.delete(address);
|
|
1000
1220
|
}
|
|
@@ -1280,12 +1500,12 @@ function normalizeProtocol(protocol, result, prices) {
|
|
|
1280
1500
|
if (bespoke) return bespoke(result, prices);
|
|
1281
1501
|
return walkProtocolResponse(result, prices);
|
|
1282
1502
|
}
|
|
1283
|
-
function clearPortfolioCache() {
|
|
1284
|
-
|
|
1503
|
+
async function clearPortfolioCache() {
|
|
1504
|
+
await getWalletCacheStore().clear();
|
|
1285
1505
|
portfolioInflight.clear();
|
|
1286
1506
|
}
|
|
1287
|
-
function clearPortfolioCacheFor(address) {
|
|
1288
|
-
|
|
1507
|
+
async function clearPortfolioCacheFor(address) {
|
|
1508
|
+
await getWalletCacheStore().delete(address);
|
|
1289
1509
|
portfolioInflight.delete(address);
|
|
1290
1510
|
}
|
|
1291
1511
|
function clearPriceMapCache() {
|
|
@@ -4532,7 +4752,7 @@ function getModifiableFields(toolName) {
|
|
|
4532
4752
|
}
|
|
4533
4753
|
|
|
4534
4754
|
// src/prompt.ts
|
|
4535
|
-
var DEFAULT_SYSTEM_PROMPT = `You are Audric \u2014 a financial agent on Sui. Audric is exactly five products: Audric Passport (the trust layer \u2014 Google sign-in, non-custodial wallet, tap-to-confirm consent, sponsored gas \u2014 wraps every other product), Audric Intelligence (you \u2014 the 5-system brain: Agent Harness with 34 tools, Reasoning Engine with
|
|
4755
|
+
var DEFAULT_SYSTEM_PROMPT = `You are Audric \u2014 a financial agent on Sui. Audric is exactly five products: Audric Passport (the trust layer \u2014 Google sign-in, non-custodial wallet, tap-to-confirm consent, sponsored gas \u2014 wraps every other product), Audric Intelligence (you \u2014 the 5-system brain: Agent Harness with 34 tools, Reasoning Engine with 14 guards and 6 skill recipes, Silent Profile, Chain Memory, AdviceLog), Audric Finance (manage money on Sui \u2014 Save via NAVI lending at 3-8% APY USDC, Credit via NAVI borrowing with health factor, Swap via Cetus aggregator across 20+ DEXs at 0.1% fee, Charts for yield/health/portfolio viz), Audric Pay (move money \u2014 send USDC, receive via payment links / invoices / QR; free, global, instant on Sui), and Audric Store (creator marketplace, ships Phase 5 \u2014 say "coming soon" if asked). Save, swap, borrow, repay, withdraw, charts \u2192 Audric Finance. Send, receive, payment-link, invoice, QR \u2192 Audric Pay. Your silent context (profile, memory, chain facts, advice log) shapes your replies but never surfaces as a notification \u2014 you act only when the user asks, and every write waits on their tap-to-confirm via Passport. You can also call 41 paid APIs (music, image, research, translation, weather, fulfilment) via MPP micropayments using the pay_api tool \u2014 this is an internal capability, not a promoted product, so only mention it when the user asks for something that needs it.
|
|
4536
4756
|
|
|
4537
4757
|
## Response rules
|
|
4538
4758
|
- 1-2 sentences max. No bullet lists unless asked. No preambles.
|
|
@@ -5966,7 +6186,7 @@ var QueryEngine = class {
|
|
|
5966
6186
|
};
|
|
5967
6187
|
if (this.walletAddress) {
|
|
5968
6188
|
this.portfolioCache?.delete(this.walletAddress);
|
|
5969
|
-
clearPortfolioCacheFor(this.walletAddress);
|
|
6189
|
+
await clearPortfolioCacheFor(this.walletAddress);
|
|
5970
6190
|
}
|
|
5971
6191
|
if (!signal.aborted) {
|
|
5972
6192
|
await new Promise((resolve) => {
|
|
@@ -7866,6 +8086,6 @@ function sanitizeAnthropicMessages(messages) {
|
|
|
7866
8086
|
return merged;
|
|
7867
8087
|
}
|
|
7868
8088
|
|
|
7869
|
-
export { AnthropicProvider, BalanceTracker, CANVAS_TEMPLATES, ContextBudget, CostTracker, DEFAULT_GUARD_CONFIG, DEFAULT_PERMISSION_CONFIG, DEFAULT_SYSTEM_PROMPT, EarlyToolDispatcher, InMemoryDefiCacheStore, McpClientManager, McpResponseCache, MemorySessionStore, NAVI_MCP_CONFIG, NAVI_MCP_URL, NAVI_SERVER_NAME, NaviTools, PERMISSION_PRESETS, QueryEngine, READ_TOOLS, RecipeRegistry, RetryTracker, TOOL_FLAGS, TOOL_MODIFIABLE_FIELDS, TxMutex, WRITE_TOOLS, activitySummaryTool, adaptAllMcpTools, adaptAllServerTools, adaptMcpTool, applyToolFlags, balanceCheckTool, borrowTool, budgetToolResult, buildCachedSystemPrompt, buildMcpTools, buildProactivenessInstructions, buildProfileContext, buildSelfEvaluationInstruction, buildStateContext, buildTool, claimRewardsTool, classifyEffort, clearPortfolioCache, clearPortfolioCacheFor, clearPriceMapCache, compactMessages, createGuardRunnerState, engineToSSE, estimateTokens, explainTxTool, extractConversationText, extractMcpText, fetchAddressDefiPortfolio, fetchAddressPortfolio, fetchAudricHistory, fetchAudricPortfolio, fetchAvailableRewards, fetchBalance, fetchHealthFactor, fetchPositions, fetchProtocolStats, fetchRates, fetchSavings, fetchTokenPrices, fetchWalletCoins, findTool, getAudricApiBase, getDefaultTools, getDefiCacheStore, getMcpManager, getModifiableFields, getToolFlags, getWalletAddress, guardArtifactPreview, guardStaleData, hasNaviMcp, healthCheckTool, loadRecipes, microcompact, mppServicesTool, parseMcpJson, parseRecipe, parseSSE, payApiTool, portfolioAnalysisTool, protocolDeepDiveTool, ratesInfoTool, registerEngineTools, renderCanvasTool, repayDebtTool, requireAgent, resetDefiCacheStore, resolvePermissionTier, resolveUsdValue, runGuards, runTools, saveContactTool, saveDepositTool, savingsInfoTool, sendTransferTool, serializeSSE, setDefiCacheStore, spendingAnalyticsTool, swapExecuteTool, swapQuoteTool, tokenPricesTool, toolNameToOperation, toolsToDefinitions, transactionHistoryTool, transformBalance, transformHealthFactor, transformPositions, transformRates, transformRewards, transformSavings, updateGuardStateAfterToolResult, validateHistory, voloStakeTool, voloStatsTool, voloUnstakeTool, webSearchTool, withdrawTool, yieldSummaryTool };
|
|
8089
|
+
export { AnthropicProvider, BalanceTracker, CANVAS_TEMPLATES, ContextBudget, CostTracker, DEFAULT_GUARD_CONFIG, DEFAULT_LEASE_SEC, DEFAULT_PERMISSION_CONFIG, DEFAULT_POLL_BUDGET_MS, DEFAULT_POLL_INTERVAL_MS, DEFAULT_SYSTEM_PROMPT, EarlyToolDispatcher, InMemoryDefiCacheStore, InMemoryFetchLock, InMemoryWalletCacheStore, McpClientManager, McpResponseCache, MemorySessionStore, NAVI_MCP_CONFIG, NAVI_MCP_URL, NAVI_SERVER_NAME, NaviTools, PERMISSION_PRESETS, QueryEngine, READ_TOOLS, RecipeRegistry, RetryTracker, TOOL_FLAGS, TOOL_MODIFIABLE_FIELDS, TxMutex, WRITE_TOOLS, activitySummaryTool, adaptAllMcpTools, adaptAllServerTools, adaptMcpTool, applyToolFlags, awaitOrFetch, balanceCheckTool, borrowTool, budgetToolResult, buildCachedSystemPrompt, buildMcpTools, buildProactivenessInstructions, buildProfileContext, buildSelfEvaluationInstruction, buildStateContext, buildTool, claimRewardsTool, classifyEffort, clearPortfolioCache, clearPortfolioCacheFor, clearPriceMapCache, compactMessages, createGuardRunnerState, engineToSSE, estimateTokens, explainTxTool, extractConversationText, extractMcpText, fetchAddressDefiPortfolio, fetchAddressPortfolio, fetchAudricHistory, fetchAudricPortfolio, fetchAvailableRewards, fetchBalance, fetchHealthFactor, fetchPositions, fetchProtocolStats, fetchRates, fetchSavings, fetchTokenPrices, fetchWalletCoins, findTool, getAudricApiBase, getDefaultTools, getDefiCacheStore, getFetchLock, getMcpManager, getModifiableFields, getToolFlags, getWalletAddress, getWalletCacheStore, guardArtifactPreview, guardStaleData, hasNaviMcp, healthCheckTool, loadRecipes, microcompact, mppServicesTool, parseMcpJson, parseRecipe, parseSSE, payApiTool, portfolioAnalysisTool, protocolDeepDiveTool, ratesInfoTool, registerEngineTools, renderCanvasTool, repayDebtTool, requireAgent, resetDefiCacheStore, resetFetchLock, resetWalletCacheStore, resolvePermissionTier, resolveUsdValue, runGuards, runTools, saveContactTool, saveDepositTool, savingsInfoTool, sendTransferTool, serializeSSE, setDefiCacheStore, setFetchLock, setWalletCacheStore, spendingAnalyticsTool, swapExecuteTool, swapQuoteTool, tokenPricesTool, toolNameToOperation, toolsToDefinitions, transactionHistoryTool, transformBalance, transformHealthFactor, transformPositions, transformRates, transformRewards, transformSavings, updateGuardStateAfterToolResult, validateHistory, voloStakeTool, voloStatsTool, voloUnstakeTool, webSearchTool, withdrawTool, yieldSummaryTool };
|
|
7870
8090
|
//# sourceMappingURL=index.js.map
|
|
7871
8091
|
//# sourceMappingURL=index.js.map
|