@bancor/carbon-sdk
Advanced tools
Comparing version 0.0.97-DEV to 0.0.98-DEV
@@ -20,3 +20,3 @@ import { CacheEvents, TypedEventEmitter } from './types'; | ||
private _checkAndHandleCacheMiss; | ||
clear(): void; | ||
clear(silent?: boolean): void; | ||
getStrategiesByPair(token0: string, token1: string): Promise<EncodedStrategy[] | undefined>; | ||
@@ -23,0 +23,0 @@ getStrategyById(id: BigNumberish): EncodedStrategy | undefined; |
@@ -1,1 +0,1 @@ | ||
import e from"events";import{fromPairKey as t,toPairKey as r,toDirectionKey as a,isOrderTradable as s}from"../utils/index.js";import{encodedStrategyStrToBN as i,encodedOrderStrToBN as d,encodedStrategyBNToStr as n,encodedOrderBNToStr as c}from"../../utils/serializers/index.js";import{Logger as o}from"../../common/logger/index.js";const h=new o("ChainCache.ts");class g extends e{_strategiesByPair={};_strategiesById={};_ordersByDirectedPair={};_latestBlockNumber=0;_latestTradesByPair={};_latestTradesByDirectedPair={};_blocksMetadata=[];_tradingFeePPMByPair={};_handleCacheMiss;static fromSerialized(e){try{const t=new g;return t._deserialize(e),t}catch(e){h.error("Failed to deserialize cache, returning clear cache",e)}return new g}_deserialize(e){const t=JSON.parse(e),{schemeVersion:r}=t;6===r?(this._strategiesByPair=Object.entries(t.strategiesByPair).reduce(((e,[t,r])=>(e[t]=r.map(i),e)),{}),this._strategiesById=Object.entries(t.strategiesById).reduce(((e,[t,r])=>(e[t]=i(r),e)),{}),this._ordersByDirectedPair=Object.entries(t.ordersByDirectedPair).reduce(((e,[t,r])=>(e[t]=Object.entries(r).reduce(((e,[t,r])=>(e[t]=d(r),e)),{}),e)),{}),this._tradingFeePPMByPair=t.tradingFeePPMByPair,this._latestBlockNumber=t.latestBlockNumber,this._latestTradesByPair=t.latestTradesByPair,this._latestTradesByDirectedPair=t.latestTradesByDirectedPair,this._blocksMetadata=t.blocksMetadata):h.log("Cache version mismatch, ignoring cache. Expected",6,"got",r,"This may be due to a breaking change in the cache format since it was last persisted.")}serialize(){const e={schemeVersion:6,strategiesByPair:Object.entries(this._strategiesByPair).reduce(((e,[t,r])=>(e[t]=r.map(n),e)),{}),strategiesById:Object.entries(this._strategiesById).reduce(((e,[t,r])=>(e[t]=n(r),e)),{}),ordersByDirectedPair:Object.entries(this._ordersByDirectedPair).reduce(((e,[t,r])=>(e[t]=Object.entries(r).reduce(((e,[t,r])=>(e[t]=c(r),e)),{}),e)),{}),tradingFeePPMByPair:this._tradingFeePPMByPair,latestBlockNumber:this._latestBlockNumber,latestTradesByPair:this._latestTradesByPair,latestTradesByDirectedPair:this._latestTradesByDirectedPair,blocksMetadata:this._blocksMetadata};return JSON.stringify(e)}setCacheMissHandler(e){this._handleCacheMiss=e}async _checkAndHandleCacheMiss(e,t){this._handleCacheMiss&&!this.hasCachedPair(e,t)&&(h.debug("Cache miss for pair",e,t),await this._handleCacheMiss(e,t),h.debug("Cache miss for pair",e,t,"resolved"))}clear(){const e=Object.keys(this._strategiesByPair).map(t);this._strategiesByPair={},this._strategiesById={},this._ordersByDirectedPair={},this._latestBlockNumber=0,this._latestTradesByPair={},this._latestTradesByDirectedPair={},this._blocksMetadata=[],this.emit("onPairDataChanged",e)}async getStrategiesByPair(e,t){await this._checkAndHandleCacheMiss(e,t);const a=r(e,t);return this._strategiesByPair[a]}getStrategyById(e){return this._strategiesById[e.toString()]}getCachedPairs(e=!0){return e?Object.entries(this._strategiesByPair).filter((([e,t])=>t.length>0)).map((([e,r])=>t(e))):Object.keys(this._strategiesByPair).map(t)}async getOrdersByPair(e,t,r=!1){await this._checkAndHandleCacheMiss(e,t);const i=a(e,t),d=this._ordersByDirectedPair[i]||{};return r?d:Object.fromEntries(Object.entries(d).filter((([e,t])=>s(t))))}hasCachedPair(e,t){const a=r(e,t);return!!this._strategiesByPair[a]}async getLatestTradeByPair(e,t){await this._checkAndHandleCacheMiss(e,t);const a=r(e,t);return this._latestTradesByPair[a]}async getLatestTradeByDirectedPair(e,t){await this._checkAndHandleCacheMiss(e,t);const r=a(e,t);return this._latestTradesByDirectedPair[r]}getLatestTrades(){return Object.values(this._latestTradesByPair)}getLatestBlockNumber(){return this._latestBlockNumber}async getTradingFeePPMByPair(e,t){await this._checkAndHandleCacheMiss(e,t);const a=r(e,t);return this._tradingFeePPMByPair[a]}get blocksMetadata(){return this._blocksMetadata}set blocksMetadata(e){this._blocksMetadata=e}addPair(e,a,s,i=!1){h.debug("Adding pair with",s.length," strategies to cache",e,a);const d=r(e,a);if(this._strategiesByPair[d])throw new Error(`Pair ${d} already cached`);this._strategiesByPair[d]=s,s.forEach((e=>{this._strategiesById[e.id.toString()]=e,this._addStrategyOrders(e)})),i||(h.debug("Emitting onPairAddedToCache",e,a),this.emit("onPairAddedToCache",t(d)))}addPairFees(e,t,a){h.debug("Adding trading fee to pair",e,t,"fee",a);const s=r(e,t);this._tradingFeePPMByPair[s]=a}applyBatchedUpdates(e,a,s,i,d,n){h.debug("Applying batched updates to cache",{latestBlockNumber:e,latestFeeUpdates:a,latestTrades:s,createdStrategies:i,updatedStrategies:d,deletedStrategies:n});const c=new Set;s.forEach((e=>{this._setLatestTrade(e),c.add(r(e.sourceToken,e.targetToken))})),i.forEach((e=>{this._addStrategy(e),c.add(r(e.token0,e.token1))})),d.forEach((e=>{this._updateStrategy(e),c.add(r(e.token0,e.token1))})),n.forEach((e=>{this._deleteStrategy(e),c.add(r(e.token0,e.token1))})),a.forEach((([e,t,a])=>{this._tradingFeePPMByPair[r(e,t)]=a})),this._setLatestBlockNumber(e),c.size>0&&(h.debug("Emitting onPairDataChanged",c),this.emit("onPairDataChanged",Array.from(c).map(t)))}_setLatestBlockNumber(e){this._latestBlockNumber=e}_setLatestTrade(e){if(!this.hasCachedPair(e.sourceToken,e.targetToken))throw new Error(`Pair ${r(e.sourceToken,e.targetToken)} is not cached, cannot set latest trade`);const t=r(e.sourceToken,e.targetToken);this._latestTradesByPair[t]=e;const s=a(e.sourceToken,e.targetToken);this._latestTradesByDirectedPair[s]=e}_addStrategyOrders(e){for(const t of[[e.token0,e.token1],[e.token1,e.token0]]){const r=a(t[0],t[1]),s=t[0]===e.token0?e.order1:e.order0,i=this._ordersByDirectedPair[r];i?i[e.id.toString()]=s:this._ordersByDirectedPair[r]={[e.id.toString()]:s}}}_removeStrategyOrders(e){for(const t of[[e.token0,e.token1],[e.token1,e.token0]]){const r=a(t[0],t[1]),s=this._ordersByDirectedPair[r];s&&(delete s[e.id.toString()],0===Object.keys(s).length&&delete this._ordersByDirectedPair.key)}}_addStrategy(e){if(!this.hasCachedPair(e.token0,e.token1))throw new Error(`Pair ${r(e.token0,e.token1)} is not cached, cannot add strategy`);const t=r(e.token0,e.token1);if(this._strategiesById[e.id.toString()])return void h.debug(`Strategy ${e.id} already cached, under the pair ${t} - skipping`);const a=this._strategiesByPair[t]||[];a.push(e),this._strategiesByPair[t]=a,this._strategiesById[e.id.toString()]=e,this._addStrategyOrders(e)}_updateStrategy(e){if(!this.hasCachedPair(e.token0,e.token1))throw new Error(`Pair ${r(e.token0,e.token1)} is not cached, cannot update strategy`);const t=r(e.token0,e.token1),a=(this._strategiesByPair[t]||[]).filter((t=>!t.id.eq(e.id)));a.push(e),this._strategiesByPair[t]=a,this._strategiesById[e.id.toString()]=e,this._removeStrategyOrders(e),this._addStrategyOrders(e)}_deleteStrategy(e){if(!this.hasCachedPair(e.token0,e.token1))throw new Error(`Pair ${r(e.token0,e.token1)} is not cached, cannot delete strategy`);const t=r(e.token0,e.token1);delete this._strategiesById[e.id.toString()];const a=(this._strategiesByPair[t]||[]).filter((t=>!t.id.eq(e.id)));this._strategiesByPair[t]=a,this._removeStrategyOrders(e)}}export{g as ChainCache}; | ||
import e from"events";import{fromPairKey as t,toPairKey as r,toDirectionKey as a,isOrderTradable as s}from"../utils/index.js";import{encodedStrategyStrToBN as i,encodedOrderStrToBN as d,encodedStrategyBNToStr as n,encodedOrderBNToStr as c}from"../../utils/serializers/index.js";import{Logger as o}from"../../common/logger/index.js";const h=new o("ChainCache.ts");class g extends e{_strategiesByPair={};_strategiesById={};_ordersByDirectedPair={};_latestBlockNumber=0;_latestTradesByPair={};_latestTradesByDirectedPair={};_blocksMetadata=[];_tradingFeePPMByPair={};_handleCacheMiss;static fromSerialized(e){try{const t=new g;return t._deserialize(e),t}catch(e){h.error("Failed to deserialize cache, returning clear cache",e)}return new g}_deserialize(e){const t=JSON.parse(e),{schemeVersion:r}=t;6===r?(this._strategiesByPair=Object.entries(t.strategiesByPair).reduce(((e,[t,r])=>(e[t]=r.map(i),e)),{}),this._strategiesById=Object.entries(t.strategiesById).reduce(((e,[t,r])=>(e[t]=i(r),e)),{}),this._ordersByDirectedPair=Object.entries(t.ordersByDirectedPair).reduce(((e,[t,r])=>(e[t]=Object.entries(r).reduce(((e,[t,r])=>(e[t]=d(r),e)),{}),e)),{}),this._tradingFeePPMByPair=t.tradingFeePPMByPair,this._latestBlockNumber=t.latestBlockNumber,this._latestTradesByPair=t.latestTradesByPair,this._latestTradesByDirectedPair=t.latestTradesByDirectedPair,this._blocksMetadata=t.blocksMetadata):h.log("Cache version mismatch, ignoring cache. Expected",6,"got",r,"This may be due to a breaking change in the cache format since it was last persisted.")}serialize(){const e={schemeVersion:6,strategiesByPair:Object.entries(this._strategiesByPair).reduce(((e,[t,r])=>(e[t]=r.map(n),e)),{}),strategiesById:Object.entries(this._strategiesById).reduce(((e,[t,r])=>(e[t]=n(r),e)),{}),ordersByDirectedPair:Object.entries(this._ordersByDirectedPair).reduce(((e,[t,r])=>(e[t]=Object.entries(r).reduce(((e,[t,r])=>(e[t]=c(r),e)),{}),e)),{}),tradingFeePPMByPair:this._tradingFeePPMByPair,latestBlockNumber:this._latestBlockNumber,latestTradesByPair:this._latestTradesByPair,latestTradesByDirectedPair:this._latestTradesByDirectedPair,blocksMetadata:this._blocksMetadata};return JSON.stringify(e)}setCacheMissHandler(e){this._handleCacheMiss=e}async _checkAndHandleCacheMiss(e,t){this._handleCacheMiss&&!this.hasCachedPair(e,t)&&(h.debug("Cache miss for pair",e,t),await this._handleCacheMiss(e,t),h.debug("Cache miss for pair",e,t,"resolved"))}clear(e=!1){const r=Object.keys(this._strategiesByPair).map(t);this._strategiesByPair={},this._strategiesById={},this._ordersByDirectedPair={},this._latestBlockNumber=0,this._latestTradesByPair={},this._latestTradesByDirectedPair={},this._blocksMetadata=[],e||this.emit("onPairDataChanged",r)}async getStrategiesByPair(e,t){await this._checkAndHandleCacheMiss(e,t);const a=r(e,t);return this._strategiesByPair[a]}getStrategyById(e){return this._strategiesById[e.toString()]}getCachedPairs(e=!0){return e?Object.entries(this._strategiesByPair).filter((([e,t])=>t.length>0)).map((([e,r])=>t(e))):Object.keys(this._strategiesByPair).map(t)}async getOrdersByPair(e,t,r=!1){await this._checkAndHandleCacheMiss(e,t);const i=a(e,t),d=this._ordersByDirectedPair[i]||{};return r?d:Object.fromEntries(Object.entries(d).filter((([e,t])=>s(t))))}hasCachedPair(e,t){const a=r(e,t);return!!this._strategiesByPair[a]}async getLatestTradeByPair(e,t){await this._checkAndHandleCacheMiss(e,t);const a=r(e,t);return this._latestTradesByPair[a]}async getLatestTradeByDirectedPair(e,t){await this._checkAndHandleCacheMiss(e,t);const r=a(e,t);return this._latestTradesByDirectedPair[r]}getLatestTrades(){return Object.values(this._latestTradesByPair)}getLatestBlockNumber(){return this._latestBlockNumber}async getTradingFeePPMByPair(e,t){await this._checkAndHandleCacheMiss(e,t);const a=r(e,t);return this._tradingFeePPMByPair[a]}get blocksMetadata(){return this._blocksMetadata}set blocksMetadata(e){this._blocksMetadata=e}addPair(e,a,s,i=!1){h.debug("Adding pair with",s.length," strategies to cache",e,a);const d=r(e,a);if(this._strategiesByPair[d])throw new Error(`Pair ${d} already cached`);this._strategiesByPair[d]=s,s.forEach((e=>{this._strategiesById[e.id.toString()]=e,this._addStrategyOrders(e)})),i||(h.debug("Emitting onPairAddedToCache",e,a),this.emit("onPairAddedToCache",t(d)))}addPairFees(e,t,a){h.debug("Adding trading fee to pair",e,t,"fee",a);const s=r(e,t);this._tradingFeePPMByPair[s]=a}applyBatchedUpdates(e,a,s,i,d,n){h.debug("Applying batched updates to cache",{latestBlockNumber:e,latestFeeUpdates:a,latestTrades:s,createdStrategies:i,updatedStrategies:d,deletedStrategies:n});const c=new Set;s.forEach((e=>{this._setLatestTrade(e),c.add(r(e.sourceToken,e.targetToken))})),i.forEach((e=>{this._addStrategy(e),c.add(r(e.token0,e.token1))})),d.forEach((e=>{this._updateStrategy(e),c.add(r(e.token0,e.token1))})),n.forEach((e=>{this._deleteStrategy(e),c.add(r(e.token0,e.token1))})),a.forEach((([e,t,a])=>{this._tradingFeePPMByPair[r(e,t)]=a})),this._setLatestBlockNumber(e),c.size>0&&(h.debug("Emitting onPairDataChanged",c),this.emit("onPairDataChanged",Array.from(c).map(t)))}_setLatestBlockNumber(e){this._latestBlockNumber=e}_setLatestTrade(e){if(!this.hasCachedPair(e.sourceToken,e.targetToken))throw new Error(`Pair ${r(e.sourceToken,e.targetToken)} is not cached, cannot set latest trade`);const t=r(e.sourceToken,e.targetToken);this._latestTradesByPair[t]=e;const s=a(e.sourceToken,e.targetToken);this._latestTradesByDirectedPair[s]=e}_addStrategyOrders(e){for(const t of[[e.token0,e.token1],[e.token1,e.token0]]){const r=a(t[0],t[1]),s=t[0]===e.token0?e.order1:e.order0,i=this._ordersByDirectedPair[r];i?i[e.id.toString()]=s:this._ordersByDirectedPair[r]={[e.id.toString()]:s}}}_removeStrategyOrders(e){for(const t of[[e.token0,e.token1],[e.token1,e.token0]]){const r=a(t[0],t[1]),s=this._ordersByDirectedPair[r];s&&(delete s[e.id.toString()],0===Object.keys(s).length&&delete this._ordersByDirectedPair.key)}}_addStrategy(e){if(!this.hasCachedPair(e.token0,e.token1))throw new Error(`Pair ${r(e.token0,e.token1)} is not cached, cannot add strategy`);const t=r(e.token0,e.token1);if(this._strategiesById[e.id.toString()])return void h.debug(`Strategy ${e.id} already cached, under the pair ${t} - skipping`);const a=this._strategiesByPair[t]||[];a.push(e),this._strategiesByPair[t]=a,this._strategiesById[e.id.toString()]=e,this._addStrategyOrders(e)}_updateStrategy(e){if(!this.hasCachedPair(e.token0,e.token1))throw new Error(`Pair ${r(e.token0,e.token1)} is not cached, cannot update strategy`);const t=r(e.token0,e.token1),a=(this._strategiesByPair[t]||[]).filter((t=>!t.id.eq(e.id)));a.push(e),this._strategiesByPair[t]=a,this._strategiesById[e.id.toString()]=e,this._removeStrategyOrders(e),this._addStrategyOrders(e)}_deleteStrategy(e){if(!this.hasCachedPair(e.token0,e.token1))throw new Error(`Pair ${r(e.token0,e.token1)} is not cached, cannot delete strategy`);const t=r(e.token0,e.token1);delete this._strategiesById[e.id.toString()];const a=(this._strategiesByPair[t]||[]).filter((t=>!t.id.eq(e.id)));this._strategiesByPair[t]=a,this._removeStrategyOrders(e)}}export{g as ChainCache}; |
@@ -11,3 +11,4 @@ import { ChainCache } from './ChainCache'; | ||
private _initialSyncDone; | ||
constructor(fetcher: Fetcher, chainCache: ChainCache); | ||
private _maxBlockAge?; | ||
constructor(fetcher: Fetcher, chainCache: ChainCache, maxBlockAge?: number); | ||
startDataSync(): Promise<void>; | ||
@@ -14,0 +15,0 @@ private _updatePairsFromChain; |
@@ -1,1 +0,1 @@ | ||
import{findAndRemoveLeading as e,toPairKey as t}from"../utils/index.js";import{Logger as a}from"../../common/logger/index.js";const s=new a("ChainSync.ts");class i{_fetcher;_chainCache;_syncCalled=!1;_slowPollPairs=!1;_pairs=[];_lastFetch=Date.now();_initialSyncDone=!1;constructor(e,t){this._fetcher=e,this._chainCache=t}async startDataSync(){if(s.debug("startDataSync called"),this._syncCalled)throw new Error("ChainSync.startDataSync() can only be called once");this._syncCalled=!0;const e=await this._fetcher.getBlockNumber();0===this._chainCache.getLatestBlockNumber()&&(s.debug("startDataSync - cache is new",arguments),this._chainCache.applyBatchedUpdates(e,[],[],[],[],[])),await this._updatePairsFromChain(),await Promise.all([this._populateFeesData(this._pairs),this._populatePairsData(),this._syncEvents()])}async _updatePairsFromChain(){s.debug("_updatePairsFromChain fetches pairs"),this._pairs=[...await this._fetcher.pairs()],s.debug("_updatePairsFromChain fetched pairs",this._pairs),this._lastFetch=Date.now(),0===this._pairs.length&&s.error("_updatePairsFromChain fetched no pairs - this indicates a problem")}async _populateFeesData(e,t=!1){if(s.debug("populateFeesData called"),0===e.length)return void s.error("populateFeesData called with no pairs - skipping");const a=t?e:e.filter((e=>!this._chainCache.hasCachedPair(e[0],e[1])));if(0===a.length)return;const i=await this._fetcher.pairsTradingFeePPM(a);s.debug("populateFeesData fetched fee updates",i),i.forEach((e=>{this._chainCache.addPairFees(e[0],e[1],e[2])}))}async _populatePairsData(){s.debug("_populatePairsData called"),this._slowPollPairs=!1;const t=async()=>{try{if(0===this._pairs.length){if(this._slowPollPairs&&Date.now()-this._lastFetch<6e4)return void setTimeout(t,1e3);await this._updatePairsFromChain()}const a=e(this._pairs,(e=>!this._chainCache.hasCachedPair(e[0],e[1])));a?(s.debug("_populatePairsData adds pair to cache",a),await this.syncPairData(a[0],a[1],!this._initialSyncDone),setTimeout(t,1)):(s.debug("_populatePairsData handled all pairs and goes to slow poll mode"),this._slowPollPairs=!0,this._initialSyncDone=!0,setTimeout(t,1e3))}catch(e){s.error("Error while syncing pairs data",e),setTimeout(t,6e4)}};setTimeout(t,1)}async syncPairData(e,t,a=!1){if(!this._syncCalled)throw new Error("ChainSync.startDataSync() must be called before syncPairData()");const s=await this._fetcher.strategiesByPair(e,t);this._chainCache.hasCachedPair(e,t)||this._chainCache.addPair(e,t,s,a)}_getBlockChunks(e,t,a){const s=[];for(let i=e;i<=t;i+=a){const e=i,h=Math.min(i+a-1,t);s.push([e,h])}return s}async _syncEvents(){s.debug("_syncEvents called");const e=async()=>{try{const a=this._chainCache.getLatestBlockNumber(),i=await this._fetcher.getBlockNumber();if(i>a){if(await this._detectReorg(i))return s.debug("_syncEvents detected reorg - resetting"),this._chainCache.clear(),this._chainCache.applyBatchedUpdates(i,[],[],[],[],[]),this._resetPairsFetching(),void setTimeout(e,1);const h=new Set(this._chainCache.getCachedPairs(!1).map((e=>t(e[0],e[1]))));s.debug("_syncEvents fetches events",a+1,i);const n=this._getBlockChunks(a+1,i,1e3);s.debug("_syncEvents block chunks",n);const r=[],c=[],o=[],l=[],d=[],u=[];for(const e of n){s.debug("_syncEvents fetches events for chunk",e);const t=await this._fetcher.getLatestStrategyCreatedStrategies(e[0],e[1]),a=await this._fetcher.getLatestStrategyUpdatedStrategies(e[0],e[1]),i=await this._fetcher.getLatestStrategyDeletedStrategies(e[0],e[1]),h=await this._fetcher.getLatestTokensTradedTrades(e[0],e[1]),_=await this._fetcher.getLatestPairTradingFeeUpdates(e[0],e[1]),p=await this._fetcher.getLatestTradingFeeUpdates(e[0],e[1]);r.push(t),c.push(a),o.push(i),l.push(h),d.push(_),u.push(p),s.debug("_syncEvents fetched the following events for chunks",n,{createdStrategiesChunk:t,updatedStrategiesChunk:a,deletedStrategiesChunk:i,tradesChunk:h,feeUpdatesChunk:_,defaultFeeUpdatesChunk:p})}const _=r.flat(),p=c.flat(),g=o.flat(),f=l.flat(),y=d.flat(),w=u.flat().length>0;s.debug("_syncEvents fetched events",_,p,g,f,y,w);const C=[];for(const e of _)this._chainCache.hasCachedPair(e.token0,e.token1)||C.push([e.token0,e.token1]);this._chainCache.applyBatchedUpdates(i,y,f.filter((e=>h.has(t(e.sourceToken,e.targetToken)))),_.filter((e=>h.has(t(e.token0,e.token1)))),p.filter((e=>h.has(t(e.token0,e.token1)))),g.filter((e=>h.has(t(e.token0,e.token1))))),w&&(s.debug("_syncEvents noticed at least one default fee update - refetching pair fees for all pairs"),await this._populateFeesData([...await this._fetcher.pairs()],!0)),C.length>0&&(s.debug("_syncEvents noticed at least one new pair created - setting slow poll mode to false"),this._slowPollPairs=!1,s.debug("_syncEvents fetching fees for the new pairs"),await this._populateFeesData(C,!0))}}catch(e){s.error("Error syncing events:",e)}setTimeout(e,1e3)};setTimeout(e,1)}_resetPairsFetching(){this._pairs=[],this._slowPollPairs=!1,this._initialSyncDone=!1}async _detectReorg(e){s.debug("_detectReorg called");const t=this._chainCache.blocksMetadata,a={};for(const i of t){const{number:t,hash:h}=i;if(t>e)return s.log("reorg detected for block number",t,"larger than current block",e,"with hash",h),!0;const n=(await this._fetcher.getBlock(t)).hash;if(h!==n)return s.log("reorg detected for block number",t,"old hash",h,"new hash",n),!0;a[t]=i}s.debug("_detectReorg no reorg detected, updating blocks metadata");const i=[];for(let t=0;t<3;t++)a[e-t]?i.push(a[e-t]):i.push(await this._fetcher.getBlock(e-t));return this._chainCache.blocksMetadata=i,s.debug("_detectReorg updated blocks metadata"),!1}}export{i as ChainSync}; | ||
import{findAndRemoveLeading as e,toPairKey as t}from"../utils/index.js";import{Logger as a}from"../../common/logger/index.js";const s=new a("ChainSync.ts");class i{_fetcher;_chainCache;_syncCalled=!1;_slowPollPairs=!1;_pairs=[];_lastFetch=Date.now();_initialSyncDone=!1;_maxBlockAge;constructor(e,t,a){this._fetcher=e,this._chainCache=t,this._maxBlockAge=a}async startDataSync(){if(s.debug("startDataSync called"),this._syncCalled)throw new Error("ChainSync.startDataSync() can only be called once");this._syncCalled=!0;const e=await this._fetcher.getBlockNumber(),t=this._chainCache.getLatestBlockNumber();0===t?(s.debug("startDataSync - cache is new",arguments),this._chainCache.applyBatchedUpdates(e,[],[],[],[],[])):void 0!==this._maxBlockAge&&e-t>this._maxBlockAge&&(s.debug(`startDataSync - cache is too old: current block ${e}, cache block ${t}`,arguments),this._chainCache.clear(!0),this._chainCache.applyBatchedUpdates(e,[],[],[],[],[])),await this._updatePairsFromChain(),await Promise.all([this._populateFeesData(this._pairs),this._populatePairsData(),this._syncEvents()])}async _updatePairsFromChain(){s.debug("_updatePairsFromChain fetches pairs"),this._pairs=[...await this._fetcher.pairs()],s.debug("_updatePairsFromChain fetched pairs",this._pairs),this._lastFetch=Date.now(),0===this._pairs.length&&s.error("_updatePairsFromChain fetched no pairs - this indicates a problem")}async _populateFeesData(e,t=!1){if(s.debug("populateFeesData called"),0===e.length)return void s.error("populateFeesData called with no pairs - skipping");const a=t?e:e.filter((e=>!this._chainCache.hasCachedPair(e[0],e[1])));if(0===a.length)return;const i=await this._fetcher.pairsTradingFeePPM(a);s.debug("populateFeesData fetched fee updates",i),i.forEach((e=>{this._chainCache.addPairFees(e[0],e[1],e[2])}))}async _populatePairsData(){s.debug("_populatePairsData called"),this._slowPollPairs=!1;const t=async()=>{try{if(0===this._pairs.length){if(this._slowPollPairs&&Date.now()-this._lastFetch<6e4)return void setTimeout(t,1e3);await this._updatePairsFromChain()}const a=e(this._pairs,(e=>!this._chainCache.hasCachedPair(e[0],e[1])));a?(s.debug("_populatePairsData adds pair to cache",a),await this.syncPairData(a[0],a[1],!this._initialSyncDone),setTimeout(t,1)):(s.debug("_populatePairsData handled all pairs and goes to slow poll mode"),this._slowPollPairs=!0,this._initialSyncDone=!0,setTimeout(t,1e3))}catch(e){s.error("Error while syncing pairs data",e),setTimeout(t,6e4)}};setTimeout(t,1)}async syncPairData(e,t,a=!1){if(!this._syncCalled)throw new Error("ChainSync.startDataSync() must be called before syncPairData()");const s=await this._fetcher.strategiesByPair(e,t);this._chainCache.hasCachedPair(e,t)||this._chainCache.addPair(e,t,s,a)}_getBlockChunks(e,t,a){const s=[];for(let i=e;i<=t;i+=a){const e=i,h=Math.min(i+a-1,t);s.push([e,h])}return s}async _syncEvents(){s.debug("_syncEvents called");const e=async()=>{try{const a=this._chainCache.getLatestBlockNumber(),i=await this._fetcher.getBlockNumber();if(i>a){if(await this._detectReorg(i))return s.debug("_syncEvents detected reorg - resetting"),this._chainCache.clear(),this._chainCache.applyBatchedUpdates(i,[],[],[],[],[]),this._resetPairsFetching(),void setTimeout(e,1);const h=new Set(this._chainCache.getCachedPairs(!1).map((e=>t(e[0],e[1]))));s.debug("_syncEvents fetches events",a+1,i);const c=this._getBlockChunks(a+1,i,1e3);s.debug("_syncEvents block chunks",c);const n=[],r=[],o=[],l=[],d=[],_=[];for(const e of c){s.debug("_syncEvents fetches events for chunk",e);const t=await this._fetcher.getLatestStrategyCreatedStrategies(e[0],e[1]),a=await this._fetcher.getLatestStrategyUpdatedStrategies(e[0],e[1]),i=await this._fetcher.getLatestStrategyDeletedStrategies(e[0],e[1]),h=await this._fetcher.getLatestTokensTradedTrades(e[0],e[1]),u=await this._fetcher.getLatestPairTradingFeeUpdates(e[0],e[1]),p=await this._fetcher.getLatestTradingFeeUpdates(e[0],e[1]);n.push(t),r.push(a),o.push(i),l.push(h),d.push(u),_.push(p),s.debug("_syncEvents fetched the following events for chunks",c,{createdStrategiesChunk:t,updatedStrategiesChunk:a,deletedStrategiesChunk:i,tradesChunk:h,feeUpdatesChunk:u,defaultFeeUpdatesChunk:p})}const u=n.flat(),p=r.flat(),g=o.flat(),f=l.flat(),y=d.flat(),k=_.flat().length>0;s.debug("_syncEvents fetched events",u,p,g,f,y,k);const w=[];for(const e of u)this._chainCache.hasCachedPair(e.token0,e.token1)||w.push([e.token0,e.token1]);this._chainCache.applyBatchedUpdates(i,y,f.filter((e=>h.has(t(e.sourceToken,e.targetToken)))),u.filter((e=>h.has(t(e.token0,e.token1)))),p.filter((e=>h.has(t(e.token0,e.token1)))),g.filter((e=>h.has(t(e.token0,e.token1))))),k&&(s.debug("_syncEvents noticed at least one default fee update - refetching pair fees for all pairs"),await this._populateFeesData([...await this._fetcher.pairs()],!0)),w.length>0&&(s.debug("_syncEvents noticed at least one new pair created - setting slow poll mode to false"),this._slowPollPairs=!1,s.debug("_syncEvents fetching fees for the new pairs"),await this._populateFeesData(w,!0))}}catch(e){s.error("Error syncing events:",e)}setTimeout(e,1e3)};setTimeout(e,1)}_resetPairsFetching(){this._pairs=[],this._slowPollPairs=!1,this._initialSyncDone=!1}async _detectReorg(e){s.debug("_detectReorg called");const t=this._chainCache.blocksMetadata,a={};for(const i of t){const{number:t,hash:h}=i;if(t>e)return s.log("reorg detected for block number",t,"larger than current block",e,"with hash",h),!0;const c=(await this._fetcher.getBlock(t)).hash;if(h!==c)return s.log("reorg detected for block number",t,"old hash",h,"new hash",c),!0;a[t]=i}s.debug("_detectReorg no reorg detected, updating blocks metadata");const i=[];for(let t=0;t<3;t++)a[e-t]?i.push(a[e-t]):i.push(await this._fetcher.getBlock(e-t));return this._chainCache.blocksMetadata=i,s.debug("_detectReorg updated blocks metadata"),!1}}export{i as ChainSync}; |
@@ -18,3 +18,3 @@ import { Fetcher } from '../common/types'; | ||
*/ | ||
export declare const initSyncedCache: (fetcher: Fetcher, cachedData?: string) => { | ||
export declare const initSyncedCache: (fetcher: Fetcher, cachedData?: string, maxBlockAge?: number) => { | ||
cache: ChainCache; | ||
@@ -21,0 +21,0 @@ startDataSync: () => Promise<void>; |
@@ -1,1 +0,1 @@ | ||
import{ChainCache as a}from"./ChainCache/index.js";import{ChainSync as n}from"./ChainSync/index.js";const t=(t,e)=>{let r;e&&(r=a.fromSerialized(e)),r||(r=new a);const i=new n(t,r);return r.setCacheMissHandler(i.syncPairData.bind(i)),{cache:r,startDataSync:i.startDataSync.bind(i)}};export{a as ChainCache,n as ChainSync,t as initSyncedCache}; | ||
import{ChainCache as a}from"./ChainCache/index.js";import{ChainSync as n}from"./ChainSync/index.js";const t=(t,e,r)=>{let i;e&&(i=a.fromSerialized(e)),i||(i=new a);const c=new n(t,i,r);return i.setCacheMissHandler(c.syncPairData.bind(c)),{cache:i,startDataSync:c.startDataSync.bind(c)}};export{a as ChainCache,n as ChainSync,t as initSyncedCache}; |
@@ -5,3 +5,3 @@ { | ||
"source": "src/index.ts", | ||
"version": "0.0.97-DEV", | ||
"version": "0.0.98-DEV", | ||
"description": "The SDK is a READ-ONLY tool, intended to facilitate working with Carbon contracts. It's a convenient wrapper around our matching algorithm, allowing programs and users get a ready to use transaction data that will allow them to manage strategies and fulfill trades", | ||
@@ -8,0 +8,0 @@ "main": "dist/index.cjs", |
@@ -45,2 +45,3 @@ # Carbon SDK | ||
let isInitializing = false; | ||
const MAX_BLOCK_AGE = 2000; // past this many blocks, the SDK won't attempt to catch up by processing events and instead call the contracts for strategy info. | ||
@@ -60,3 +61,3 @@ const init = async ( | ||
api = new ContractsApi(provider, config); | ||
const { cache, startDataSync } = initSyncedCache(api.reader, cachedData); | ||
const { cache, startDataSync } = initSyncedCache(api.reader, cachedData, MAX_BLOCK_AGE); | ||
sdkCache = cache; | ||
@@ -85,18 +86,19 @@ carbonSDK = new Toolkit( | ||
### 1. The SDK Logger supports 3 verbosity levels: | ||
- `0` (default) only prints errors and important logs. | ||
- `1` (debug) prints highly verbose logs. | ||
- `2` (debug readable) is same as `1` but also converts any BigNumber to an easy to read string (impacting performance). | ||
To use it in Node, set the environment variable `CARBON_DEFI_SDK_VERBOSITY` to the desired level. | ||
To use it from a browser app do, before importing the SDK: | ||
```js | ||
window.CARBON_DEFI_SDK_VERBOSITY = 2; | ||
``` | ||
- `0` (default) only prints errors and important logs. | ||
- `1` (debug) prints highly verbose logs. | ||
- `2` (debug readable) is same as `1` but also converts any BigNumber to an easy to read string (impacting performance). | ||
To use it in Node, set the environment variable `CARBON_DEFI_SDK_VERBOSITY` to the desired level. | ||
To use it from a browser app do, before importing the SDK: | ||
```js | ||
window.CARBON_DEFI_SDK_VERBOSITY = 2; | ||
``` | ||
### 2. For usage with contracts version < 5, without the enhanced range for trade by source: | ||
```js | ||
window.LEGACY_TRADE_BY_SOURCE_RANGE = true; | ||
``` | ||
```js | ||
window.LEGACY_TRADE_BY_SOURCE_RANGE = true; | ||
``` | ||
@@ -103,0 +105,0 @@ ## Authors |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
922210
7726
118