UNPKG

@ixily/activ

Version:

Alpha Capture Trade Idea Verification. Blockchain ownership proven trade ideas and strategies.

807 lines (754 loc) 22.8 kB
import { CONTRACT_INTERFACES, CONTRACT_TOOLS, CryptoIdeasModule, CryptoJs, IContract, IStrategyChain, IStrategyIdeaChain, IStrategyIdeaStageChain, NftModule, RULES, cachePlusRemove, cachePlusRetrieve, cachePlusUpsert, deserializeDataTool, LogModule as log, serializeDataTool, } from '../..' import { contractDbVersion } from '../../../version-contract-db.json' // import fs from 'fs' const getStrategyChain = async ( contract: IContract, strategyKey: string, creator?: string, inflateImages: boolean = true, nftIdReduceStep?: number, cached?: { ideas: IStrategyIdeaChain[] nullifiedIdeas: Map<number, Set<number>> cachedStrategy: CONTRACT_INTERFACES.IStrategyState }, ): Promise<IStrategyChain> => { // console.log('{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}}{}{}') // console.log('cached') // console.log(cached) if (nftIdReduceStep !== undefined) { if (cached !== undefined) { if (nftIdReduceStep <= cached.cachedStrategy.lastNftId) { return { strategyKey, ideas: cached.ideas, } } } } creator = creator || (await contract.gate!.signer.getAddress()) // console.log('creator') // console.log(creator) // console.log('strategyKey') // console.log(strategyKey) // console.log('contract.gate?.address') // console.log(contract.gate?.address) const ideasListDirty = await NftModule.view.general.listIdeas( creator, strategyKey, contract, ) const ideasList: number[] = [] // let ideasList: number[] = [] // console.log('ideasListDirty') // console.log(ideasListDirty) const alreadyIdeas = new Set<number>() for (const ideaKey of ideasListDirty) { if (!alreadyIdeas.has(ideaKey)) { ideasList.push(ideaKey) alreadyIdeas.add(ideaKey) } } log.dev('ideasList lenght: ', ideasList.length) log.dev('cached?.ideas.length', cached?.ideas.length) // TODO: REMOVE THIS HACK // cleaideasList = [ideasList[0]] const ideas: IStrategyIdeaChain[] = [] type PromGen = () => Promise<void> const promisesOfProccessIdeas: PromGen[] = [] let ideaIndexInsideCacheOffset = 0 let howManyNullifiedIdeas = 0 if (cached !== undefined) { const alreadyCheckKeys = new Set<number>() for (const [key, value] of cached.nullifiedIdeas.entries()) { if (!alreadyCheckKeys.has(key)) { alreadyCheckKeys.add(key) const nullifiedStages = value if (nullifiedStages.has(0)) { howManyNullifiedIdeas++ } } } } for (const ideaIndexFluid in ideasList) { const ideaIndex = Number(ideaIndexFluid) const ideaKey = ideasList[ideaIndex] const promOfProcId = async () => { const tryFromCache = async (): Promise<boolean> => { if (cached !== undefined) { if ( cached.ideas.length + howManyNullifiedIdeas > ideaIndex ) { const nullifiedStages = cached.nullifiedIdeas.get(ideaKey) if (nullifiedStages !== undefined) { if (nullifiedStages.has(0)) { ideaIndexInsideCacheOffset++ return true } } const ideaIndexInsideCache = ideaIndex - ideaIndexInsideCacheOffset //console.log('-------------------------') //console.log('-------------------------') //console.log('-------------------------') //console.log('howManyNullifiedIdeas') //console.log(howManyNullifiedIdeas) //console.log('howManyNullifiedIdeas') //console.log(howManyNullifiedIdeas) //console.log('ideaIndex') //console.log(ideaIndex) //console.log('cached.nullifiedIdeas:') //for (const [ // key, // value, //] of cached.nullifiedIdeas.entries()) { // console.log(' key') // console.log(key) // console.log(' value') // console.log(value) //} // console.log('cached.ideas.length') // console.log(cached.ideas.length) // console.log('cached.ideas[ideaIndex].ideaKey') // console.log(cached.ideas[ideaIndexInsideCache].ideaKey) // console.log('ideaKey') // console.log(ideaKey) // console.log( // 'cached.ideas[ideaIndexInsideCache].ideaKey === ideaKey', // ) // console.log( // cached.ideas[ideaIndexInsideCache].ideaKey === // ideaKey, // ) if ( cached.ideas[ideaIndexInsideCache].ideaKey === ideaKey ) { const isDecryptedStage = ( stage: IStrategyIdeaStageChain, ): boolean => { return typeof stage.ideaStage.idea !== 'string' } const firstEncryptedStage = cached.ideas[ ideaIndexInsideCache ].stages.find((one) => !isDecryptedStage(one)) if (firstEncryptedStage === undefined) { // console.log('cached.ideas[ideaIndex]') // console.log(cached.ideas[ideaIndex]) // console.log('ideaKey') // console.log(ideaKey) // console.log('firstEncryptedStage') // console.log(firstEncryptedStage) const closeStage = cached.ideas[ ideaIndexInsideCache ].stages.find( (one) => ( one.ideaStage .idea as CONTRACT_INTERFACES.ITradeIdeaIdea ).kind === 'close', ) if (closeStage !== undefined) { ideas.push( cached.ideas[ideaIndexInsideCache], ) return true } else { // console.log('closeStage') // console.log(closeStage) // throw new Error('actually not closed') const stagesListDirty = await NftModule.view.general.listStages( creator!, strategyKey, ideaKey, contract, ) // eliminate nullified stages const stagesList: number[] = [] if (cached?.nullifiedIdeas !== undefined) { const nullifiedStages = cached.nullifiedIdeas.get(ideaKey) if (nullifiedStages !== undefined) { for (const stageKey of stagesListDirty) { if ( !nullifiedStages.has( stageKey, ) ) { stagesList.push(stageKey) } } } else { stagesList.push(...stagesListDirty) } } else { stagesList.push(...stagesListDirty) } // console.log('stagesList') // console.log(stagesList) const stages = await Promise.all( stagesList.map(async (ideaStageKey) => { const seekStage = cached.ideas[ ideaIndexInsideCache ].stages.find( (one) => one.ideaStageKey === ideaStageKey, ) if (seekStage !== undefined) { return seekStage } else { // console.log('ideaStageKey') // console.log(ideaStageKey) const id = await NftModule.view.general.getIdeaNftIdByKeys( creator!, strategyKey, ideaKey, ideaStageKey, contract, ) const ideaStage = await CryptoIdeasModule.getRestoredIdeaByNftId( id, contract, inflateImages, ) // console.log('ideaStage:') // console.log(ideaStage) // if (ideaStageKey === 1) { // throw new Error('stop') // } ideaStage.content.ideaNftId = Number(id) ideaStage.nftId = Number(id) ideaStage.content.ideaKey = ideaKey ideaStage.content.ideaStageKey = ideaStageKey const stage: IStrategyIdeaStageChain = { ideaStageKey, ideaStage, } return stage } }), ) if (stages.length !== 0) { const idea: IStrategyIdeaChain = { ideaKey, stages, } ideas.push(idea) } return true } } } else { throw new Error( 'getStrategyChain: cached ideaKey does not match', ) } } } return false } const gotFromCache = await tryFromCache() // console.log('gotFromCache:') // console.log(gotFromCache) if (!gotFromCache) { const stagesList = await NftModule.view.general.listStages( creator!, strategyKey, ideaKey, contract, ) if (stagesList.length > 0) { const stages = await Promise.all( stagesList.map(async (ideaStageKey) => { log.dev('strategyKey: ' + strategyKey) log.dev('ideaKey: ' + ideaKey) log.dev('ideaStageKey: ' + ideaStageKey) // console.log('pre error Mark 1') const id = await NftModule.view.general.getIdeaNftIdByKeys( creator!, strategyKey, ideaKey, ideaStageKey, contract, ) // console.log('id') // console.log(id) // console.log('pre error Mark 2') const ideaStage = await CryptoIdeasModule.getRestoredIdeaByNftId( id, contract, inflateImages, ) // console.log('pre error Mark 3') ideaStage.content.ideaNftId = Number(id) ideaStage.nftId = Number(id) ideaStage.content.ideaKey = ideaKey ideaStage.content.ideaStageKey = ideaStageKey const stage: IStrategyIdeaStageChain = { ideaStageKey, ideaStage, } return stage }), ) const idea: IStrategyIdeaChain = { ideaKey, stages, } ideas.push(idea) } } } promisesOfProccessIdeas.push(promOfProcId) } // console.log('promisesOfProccessIdeas.length') // console.log(promisesOfProccessIdeas.length) const MAX_PROMISES_PER_STEP = 200 // if the number of promises is too big, we need to split it into smaller steps const promisesCount = promisesOfProccessIdeas.length if (promisesCount > MAX_PROMISES_PER_STEP) { const stepsCount = Math.ceil(promisesCount / MAX_PROMISES_PER_STEP) for (let i = 0; i < stepsCount; i++) { const start = i * MAX_PROMISES_PER_STEP const end = Math.min((i + 1) * MAX_PROMISES_PER_STEP, promisesCount) await Promise.all( promisesOfProccessIdeas.slice(start, end).map((each) => each()), ) // rest a fair bit await new Promise((resolve) => setTimeout(resolve, 200)) } } else { await Promise.all(promisesOfProccessIdeas.map((each) => each())) } // for (const proc of promisesOfProccessIdeas) { // await proc() // } // sort to order by ideaKey ideas.sort((a, b) => { return a.ideaKey - b.ideaKey }) log.dev('ideas', ideas) const strategy: IStrategyChain = { strategyKey, ideas, } return strategy } const chronologicalIdeasFromChain = ( chain: IStrategyChain, ): CONTRACT_INTERFACES.ITradeIdea[] => { const results: CONTRACT_INTERFACES.ITradeIdea[] = [] for (const each of chain.ideas) { for (const eachIn of each.stages) { results.push(eachIn.ideaStage) } } results.sort((a, b) => { return a.content.ideaNftId! - b.content.ideaNftId! }) return results } const getActiveIdeasFromChain = ( chain: IStrategyChain, ): CONTRACT_INTERFACES.ITradeIdea[] => { const results: CONTRACT_INTERFACES.ITradeIdea[] = [] const chronos = chronologicalIdeasFromChain(chain) const mapOfActiveTickers = new Map<string, CONTRACT_INTERFACES.ITradeIdea>() for (const each of chronos) { const ide = each.idea as CONTRACT_INTERFACES.ITradeIdeaIdea if (ide.kind === 'close' && mapOfActiveTickers.has(ide.asset.ticker)) { mapOfActiveTickers.delete(ide.asset.ticker) } else { mapOfActiveTickers.set(ide.asset.ticker, each) } } for (const each of mapOfActiveTickers.entries()) { results.push(each[1]) } return results } const postValidationCalculateSummaryCalc = ( strategyState: CONTRACT_INTERFACES.IStrategyState, ): CONTRACT_INTERFACES.ITradeIdeaStrategSumaryCalc => { let totalIdeas: number = 0 let totalClosedIdeas: number = 0 let successIdeas: number = 0 let lossesIdeas: number = 0 let lossesPoints: number = 0 let successPoints: number = 0 const lastStages = CONTRACT_TOOLS.queryChain.getOnlyLastStageOfEachIdea(strategyState) for (const lastStage of lastStages) { totalIdeas++ if (typeof lastStage.idea !== 'string') { const idea = lastStage.idea as CONTRACT_INTERFACES.ITradeIdeaIdea if (idea.kind === 'close') { totalClosedIdeas++ const isProfit = idea.result!.summary.profit if (isProfit) { successIdeas++ successPoints += Math.abs(idea.result!.summary.percentage) } else { lossesIdeas++ lossesPoints += Math.abs(idea.result!.summary.percentage) } } } } const summaryCalc: CONTRACT_INTERFACES.ITradeIdeaStrategSumaryCalc = { totalIdeas, totalClosedIdeas, successIdeas, lossesIdeas, lossesPoints, successPoints, } return summaryCalc } const getValidatedStrategyChain = async ( contract: IContract, strategyKey: string, creator?: string, inflateImages: boolean = true, nftIdReduceStep?: number, cached?: { ideas: IStrategyIdeaChain[] nullifiedIdeas: Map<number, Set<number>> cachedStrategy: CONTRACT_INTERFACES.IStrategyState // validatedPricing: Set<number> }, ): Promise<{ result: IStrategyChain cache: { ideas: IStrategyIdeaChain[] nullifiedIdeas: Map<number, Set<number>> cachedStrategy: CONTRACT_INTERFACES.IStrategyState // validatedPricing: Set<number> } }> => { // console.log('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=') // console.log('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=') // console.log('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=') // console.log('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=') // console.log('cached:') // console.log(cached) // console.log(cached?.ideas) // console.log(cached?.ideas[0]?.stages[0]?.ideaStage?.idea) // const strategyChain = await getStrategyChain( contract, strategyKey, creator, inflateImages, nftIdReduceStep, cached, ) // fs.writeFileSync('strategyChain.json', JSON.stringify(strategyChain)) // if (1 + 1 === 2) { // throw new Error('check temp chain stored') // } // const strategyChain = JSON.parse( // fs.readFileSync('strategyChain.json', 'utf8'), // ) as IStrategyChain // console.log('strategyChain:') // console.log(strategyChain) // console.log('strategyChain.ideas[0].ideaStages') // try { // for (const stuff of strategyChain.ideas[0].ideaStages) { // console.log(stuff) // } // } catch (e) { // throw e // } const chronos = chronologicalIdeasFromChain(strategyChain) // console.log('chronos.length:') // console.log(chronos.length) // console.log('cached?.ideas.length') // console.log(cached?.ideas.length) // console.log('cached?.cachedStrategy.totalIdeas') // console.log(cached?.cachedStrategy.totalIdeas) // console.log(chronos.map((each) => each.idea!)) //for (const eachIndex in chronos) { // const each = chronos[eachIndex] // console.log('============>') // console.log('Idea #' + eachIndex) // console.log('each.idea!') // console.log(each.idea!) // console.log('each.idea!.idea') // console.log(each.idea!.idea) // } // throw new Error('stop') const toCheck = chronos.slice( cached?.cachedStrategy !== undefined ? cached.cachedStrategy.totalIdeas - 1 : 0, ) // const chronos = JSON.parse( // fs.readFileSync('toCheck.json', 'utf8'), // ) as CONTRACT_INTERFACES.ITradeIdea[] // let toCheck = copyObj(chronos) // console.log('toCheck') // console.log(toCheck) // console.log('toCheck') // console.log(toCheck) // console.log('toCheck[0].creator.companyLogo') // console.log(toCheck[0]?.creator.companyLogo) // console.log('(toCheck[0].idea as ITradeIdeaIdea).asset.image') // console.log((toCheck[0].idea as ITradeIdeaIdea).asset.image) // console.log('toCheck[0].strategy.image') // console.log('toCheck[toCheck.length-2].strategy.image') // console.log(toCheck[toCheck.length - 2].strategy.image) // console.log('toCheck[toCheck.length-1].strategy.image') // console.log(toCheck[toCheck.length - 1].strategy.image) // console.log('cached?.cachedStrategy.strategy.image') // console.log(cached?.cachedStrategy.strategy.image) // fs.writeFileSync('toCheck.json', JSON.stringify(toCheck)) // if toCheck list of idea is greater than 50, we need to split it into smaller steps. // We do separate in ordered chunks of 50 ideas. // After that, we will calculate the hash of each chunk serialized. // We will use the hashes to check if the chunk is already in the cache. // we look for any chunk that is already in the cache. // if it is, we load the cache and continue from this cache and we calculate the rules with next chunk. // in a positive result, we do store it in cache using the chunk hash as key. // if we complete the rules calculation, we delete the cache and return the result. // we do this using following functions: // deserializeDataTool, // serializeDataTool, // cachePlusUpsert, // cachePlusRetrieve, // cachePlusRemove, let valid: | CONTRACT_INTERFACES.IRuleValidResult | string | { valid: true cachedStrategyState: | CONTRACT_INTERFACES.IStrategyState | undefined } const MAX_CHUNK_SIZE = 10 let toRemoveLaterPriorToReturn: (() => Promise<void>) | undefined = undefined if (toCheck.length > MAX_CHUNK_SIZE) { const chunks: CONTRACT_INTERFACES.ITradeIdea[][] = [] for (let i = 0; i < toCheck.length; i += MAX_CHUNK_SIZE) { chunks.push(toCheck.slice(i, i + MAX_CHUNK_SIZE)) } const hashes: string[] = [] for (const chunk of chunks) { const chunkHash = 'ck-' + contractDbVersion + '-' + contract.gate!.address + '-' + CryptoJs.SHA1(serializeDataTool(chunk)).toString() hashes.push(chunkHash) } const featureKey = 'stgChaCks:' let lastStrategyState: CONTRACT_INTERFACES.IStrategyState | undefined = undefined let lastChunkCalculated: number = -1 log.prod( 'Computing verify of strategy "' + strategyKey + '" with ' + chunks.length + ' chunks', ) for (const hashIndex in hashes) { // try to find stored cache with this hash and const cachedChunkResult = await cachePlusRetrieve( featureKey, featureKey + hashes[hashIndex], ) if (cachedChunkResult !== undefined) { log.prod( 'Found cached chunk: ' + hashes[hashIndex] + ' (' + hashIndex + ')', ) const cachedChunk = deserializeDataTool( cachedChunkResult, ) as CONTRACT_INTERFACES.IStrategyState valid = { valid: true, cachedStrategyState: cachedChunk, } lastStrategyState = cachedChunk lastChunkCalculated = +hashIndex break } } // console.log('---------- emergengy debug ----------') // const emergencyRead = deserializeDataTool( // fs.readFileSync('emergencySituationStore52.txt', 'utf8'), // ) as CONTRACT_INTERFACES.IStrategyState // valid = { // valid: true, // cachedStrategyState: emergencyRead, // } // lastStrategyState = emergencyRead // lastChunkCalculated = 52 // lastChunkCalculated always points to the last chunk calculated. while (lastChunkCalculated < chunks.length - 1) { log.prod('Calculating chunk: ' + (lastChunkCalculated + 1)) const chunk = chunks[lastChunkCalculated + 1] valid = await RULES.v1.rules(chunk, lastStrategyState) // if (typeof valid === 'string') { // console.log('valid') // console.log(valid) // throw new Error('ck') // } if (typeof valid === 'string') { // before thou, we do remove the cache if lastChunkCalculated is not -1 if (lastChunkCalculated !== -1) { await cachePlusRemove( featureKey + hashes[lastChunkCalculated], ) } throw new Error( 'CONTRACT RULES ERROR: Invalid strategy chain: ' + valid, ) } lastStrategyState = valid.cachedStrategyState! await cachePlusUpsert( featureKey, featureKey + hashes[lastChunkCalculated + 1], serializeDataTool(lastStrategyState), ) await cachePlusRemove(featureKey + hashes[lastChunkCalculated]) lastChunkCalculated++ if (lastChunkCalculated >= chunks.length - 1) { toRemoveLaterPriorToReturn = async () => { await cachePlusRemove( featureKey + hashes[lastChunkCalculated], ) } } } log.prod('All chunks calculated') } else { valid = toCheck.length === 0 ? { valid: true, cachedStrategyState: cached?.cachedStrategy, } : await RULES.v1.rules(toCheck, cached?.cachedStrategy) } // console.log('chronos AFTER:') // console.log(chronos.map((each) => each.idea!)) // console.log('valid:') // console.log(valid!) if (typeof valid! === 'string') { throw new Error( 'CONTRACT RULES ERROR: Invalid strategy chain: ' + valid, ) } // Now we need to validate pricing and apply right timestamps. // ACTUALLY above now happens on rules. /* const validatedPricing = cached?.validatedPricing !== undefined ? cached?.validatedPricing : new Set<number>() for (const idea of chronos) { if (idea.nftId === undefined) { throw new Error( 'getValidatedStrategyChain: idea.nftId is undefined', ) } if (!validatedPricing.has(idea.nftId!)) { await ProvableModule.validatePricingSignature(idea) validatedPricing.add(idea.nftId!) } } */ // Clean Up the nullified ideas. const nullifiedIdeas = cached !== undefined ? cached.nullifiedIdeas : new Map<number, Set<number>>() for (const ideaIndex in strategyChain.ideas) { for (const stageIndex in strategyChain.ideas[ideaIndex].stages) { const stage = strategyChain.ideas[ideaIndex].stages[stageIndex] if (stage.ideaStage.nullified !== undefined) { strategyChain.ideas[ideaIndex].stages.splice( Number(stageIndex), 1, ) let nullifiedIdea = nullifiedIdeas.get( stage.ideaStage.content.ideaKey!, ) if (nullifiedIdea === undefined) { nullifiedIdeas.set( stage.ideaStage.content.ideaKey!, new Set<number>(), ) nullifiedIdea = nullifiedIdeas.get( stage.ideaStage.content.ideaKey!, ) } nullifiedIdea!.add(stage.ideaStage.content.ideaStageKey!) } if (strategyChain.ideas[ideaIndex].stages.length === 0) { strategyChain.ideas.splice(Number(ideaIndex), 1) } } } // Completing Missing PostProcessing Data // by computing summaryCalc if (valid!.cachedStrategyState !== undefined) { valid!.cachedStrategyState!.strategy.summaryCalc = postValidationCalculateSummaryCalc(valid!.cachedStrategyState!) } if (toRemoveLaterPriorToReturn !== undefined) { await toRemoveLaterPriorToReturn!() } return { result: strategyChain, cache: { nullifiedIdeas, ideas: strategyChain.ideas, cachedStrategy: valid!.cachedStrategyState!, }, } } export const StrategyChainModule = { getStrategyChain, getValidatedStrategyChain, chronologicalIdeasFromChain, getActiveIdeasFromChain, }