UNPKG

@ixily/activ

Version:

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

1,085 lines (1,027 loc) 35.4 kB
import { default as CryptoJs } from 'crypto-js' import { BigNumber } from 'bignumber.js' import { CONTRACT_INTERFACES, CONTRACT_TOOLS } from '../' import { validateBasicEncryptedIdea, validateCloseIdeaWithinSelf, validatePartialIdeaEncryptedWithinSelf, validatePartialIdeaWithinSelf, } from '../' import { ITradeIdea } from 'contract/interfaces' const getInitialStrategyState = ( firstIdea: CONTRACT_INTERFACES.ITradeIdea, ): CONTRACT_INTERFACES.IStrategyState => { const isPublic = typeof firstIdea.idea !== 'string' const createdAt = firstIdea.strategy.createdAt firstIdea.strategy.changedAt = createdAt const state: CONTRACT_INTERFACES.IStrategyState = { strategy: { ...firstIdea.strategy, createdAt, changedAt: createdAt, summaryCalc: undefined, portfolio: undefined, portfolioAllocated: undefined, }, creator: { name: firstIdea.creator.name, company: firstIdea.creator.company, url: firstIdea.creator.url, companyLogo: firstIdea.creator.companyLogo, // backgroundImage: firstIdea.creator.backgroundImage, walletAddress: firstIdea.creator.walletAddress, }, lastNftId: 0, totalIdeas: 0, lastValidatedNftId: 0, totalValidatedIdeas: 0, orderedIdeasNft: [], ideas: new Map<number, CONTRACT_INTERFACES.ITradeIdea>(), encryptedIdeasByKey: new Map<number, number[]>(), validatedIdeasByKey: new Map<number, number[]>(), orderedEncryptedIdeaKeys: [], encryptedIdeas: new Set<number>(), validatedIdeaKeys: new Set<number>(), notNullIdeasByKey: new Map<number, number[]>(), nullifiedStagesNft: new Set<number>(), percentagePublicValidated: 0, allocationMap: new Map<string, number>(), totalAllocated: 0, firstIdeaDateChecked: false, } return state } // sort by nftId const sortIdeasChronological = ( strategyIdeas: CONTRACT_INTERFACES.ITradeIdea[], ): CONTRACT_INTERFACES.ITradeIdea[] => { const ideasChronological = strategyIdeas.sort((a, b) => { if (a.nftId === undefined) return 1 if (b.nftId === undefined) return -1 return a.nftId! - b.nftId! }) return ideasChronological } const includeIdea = ( ideaComplete: CONTRACT_INTERFACES.ITradeIdea, cachedStrategyState: CONTRACT_INTERFACES.IStrategyState, ) => { cachedStrategyState.ideas.set(ideaComplete.nftId!, ideaComplete) cachedStrategyState.totalIdeas++ } const includeEncryptedIdea = ( ideaComplete: CONTRACT_INTERFACES.ITradeIdea, cachedStrategyState: CONTRACT_INTERFACES.IStrategyState, ) => { const ideaKey = ideaComplete.content.ideaKey! if (cachedStrategyState.encryptedIdeasByKey.has(ideaKey)) { cachedStrategyState.encryptedIdeasByKey .get(ideaKey)! .push(ideaComplete.nftId!) } else { cachedStrategyState.encryptedIdeasByKey.set(ideaKey, [ ideaComplete.nftId!, ]) cachedStrategyState.orderedEncryptedIdeaKeys.push(ideaKey) } cachedStrategyState.encryptedIdeas.add(ideaComplete.nftId!) includeIdea(ideaComplete, cachedStrategyState) } const calculateTradeIdeaHash = ( idea: CONTRACT_INTERFACES.IChainIdea, ): string => { const ideaToCheck: CONTRACT_INTERFACES.ITradeIdeaIdea = { ...idea.idea } // delete ideaToCheck.chainIdeas // delete ideaToCheck.result // console.log('ideaToCheck') // console.log(ideaToCheck) const deterministicIdeaString = CONTRACT_TOOLS.deterministicStringify( ideaToCheck, ) as string if (deterministicIdeaString === undefined) { throw new Error( 'Calculate Idea Hash Error: deterministicIdeaString is undefined', ) } return CryptoJs.SHA512(deterministicIdeaString).toString() } const retrievePastStages = ( ideaComplete: CONTRACT_INTERFACES.ITradeIdea, cachedStrategyState: CONTRACT_INTERFACES.IStrategyState, ): CONTRACT_INTERFACES.ITradeIdea[] => { // console.log('cachedStrategyState.validatedIdeasByKey') // console.log(cachedStrategyState.validatedIdeasByKey) // console.log('ideaComplete.content.ideaKey!') // console.log(ideaComplete.content.ideaKey!) const validatedOnSameKeyIdeas = cachedStrategyState.validatedIdeasByKey.get( ideaComplete.content.ideaKey!, )! // console.log('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-') // console.log('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-') // console.log('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-') // console.log('validatedOnSameKeyIdeas') // console.log(validatedOnSameKeyIdeas) const validatingIdeas = [] as CONTRACT_INTERFACES.ITradeIdea[] for (const validatedOnSameKeyIdeaNftId of validatedOnSameKeyIdeas) { const validatedIdea = cachedStrategyState.ideas.get( validatedOnSameKeyIdeaNftId, )! validatingIdeas.push(validatedIdea) } // console.log('validatingIdeas') // console.log(validatingIdeas) return validatingIdeas } const validateProccessPublicIdea = async ( ideaComplete: CONTRACT_INTERFACES.ITradeIdea, cachedStrategyState: CONTRACT_INTERFACES.IStrategyState, ): Promise<string | undefined> => { const idea = ideaComplete.idea as CONTRACT_INTERFACES.ITradeIdeaIdea const alreadyMinted = ideaComplete.content.ideaKey !== undefined && ideaComplete.nftId !== undefined // console.log('idea') // console.log(idea) // console.log('alreadyMinted') // console.log(alreadyMinted) // console.log('ideaComplete.content.ideaKey') // console.log(ideaComplete.content.ideaKey) // console.log('ideaComplete.nftId') // console.log(ideaComplete.nftId) // console.log('ideaComplete.content.ideaKey!') // console.log(ideaComplete.content.ideaKey!) // console.log('cachedStrategyState.validatedIdeasByKey') // console.log( // cachedStrategyState.validatedIdeasByKey.get( // ideaComplete.content.ideaKey!, // ), // ) // console.log('ideaComplete.nftId!') // console.log(ideaComplete.nftId!) let violation: string | undefined = undefined if (idea.kind !== 'open') { if (ideaComplete.content.ideaKey === undefined) { return 'The ideaKey is required.' } } // console.log('ideaComplete pre') // console.log(ideaComplete) const validatingIdeas = idea.kind === 'open' ? [] : retrievePastStages(ideaComplete, cachedStrategyState) // console.log('ideaComplete after') // console.log(ideaComplete) violation = validatePartialIdeaWithinSelf( [...validatingIdeas, ideaComplete], cachedStrategyState, !alreadyMinted, ) if (violation !== undefined) { return violation } // validate pricing const priceValidationResult = await CONTRACT_TOOLS.validatePricingSignature( idea, cachedStrategyState, ) if (!priceValidationResult.valid) { violation = `The pricing signatures is not valid.` } if (violation === undefined) { setLastValidatedNftId(cachedStrategyState) } /* // try to find the open ideaKey const openIdeaSearch = cachedStrategyState.orderedIdeasNft .filter( (ones) => !cachedStrategyState.nullifiedStagesNft.has(ones), ) .map(cachedStrategyState.ideas.get) .reverse() .find((one) => { if (typeof one!.idea !== 'string') { if ((one!.idea as ITradeIdeaIdea).kind === 'open') { if ( (one!.idea as ITradeIdeaIdea).asset.ticker === (ideaComplete.idea as ITradeIdeaIdea).asset .ticker ) { return true } } } return false }) if(openIdeaSearch === undefined) { return `The ticker ${(ideaComplete.idea as ITradeIdeaIdea).asset.ticker} is not defined in the strategy.` } else { } */ return violation } const validateProccessEncryptedIdea = async ( ideaComplete: CONTRACT_INTERFACES.ITradeIdea, cachedStrategyState: CONTRACT_INTERFACES.IStrategyState, ): Promise<string | undefined> => { const alreadyMinted = ideaComplete.content.ideaKey !== undefined && ideaComplete.nftId !== undefined let violation = validatePartialIdeaEncryptedWithinSelf( ideaComplete, cachedStrategyState, !alreadyMinted, ) return violation } const setLastValidatedNftId = ( cachedStrategyState: CONTRACT_INTERFACES.IStrategyState, ) => { const orderedNotNull = cachedStrategyState.orderedIdeasNft .filter((ones) => !cachedStrategyState.nullifiedStagesNft.has(ones)) .map((each) => cachedStrategyState.ideas.get(each)!) let lastOne = 0 for (const ordered of orderedNotNull) { if ( !cachedStrategyState.encryptedIdeasByKey.has( ordered!.content!.ideaKey!, ) ) { lastOne = ordered!.nftId! } else { break } } cachedStrategyState.lastValidatedNftId = lastOne !== undefined ? lastOne : 0 } const validateProccessCloseDecryptIdea = async ( ideaComplete: CONTRACT_INTERFACES.ITradeIdea, cachedStrategyState: CONTRACT_INTERFACES.IStrategyState, ): Promise<string | undefined> => { const idea = ideaComplete.idea as CONTRACT_INTERFACES.ITradeIdeaIdea const encryptedIdeaNfts = cachedStrategyState.encryptedIdeasByKey.get( ideaComplete.content.ideaKey!, )! const encryptedIdeas = [] as CONTRACT_INTERFACES.ITradeIdea[] for (const encryptedIdeaNftId of encryptedIdeaNfts) { const encryptedIdea = cachedStrategyState.ideas.get(encryptedIdeaNftId)! encryptedIdeas.push(encryptedIdea) } // validate close parts hashes // check hashes if (idea.chainIdeas === undefined) { return `The chainIdeas is not defined in the content.` } for (const chainedIdeaIndex in idea.chainIdeas) { const chainedIdea = idea.chainIdeas[+chainedIdeaIndex] let actualHash: string try { actualHash = calculateTradeIdeaHash(chainedIdea) } catch (err: any) { if (err.message) return err.message throw err } // console.log('chainedIdea') // console.log(chainedIdea) // console.log('actualHash') // console.log(actualHash) // console.log('chainedIdea.ideaSha512Hash') // console.log(chainedIdea.ideaSha512Hash) if (actualHash !== chainedIdea.ideaSha512Hash) { return `The chainIdeas hashes are not valid.` } const pastNftIdeaHash = encryptedIdeas[+chainedIdeaIndex].ideaSha512Hash if (pastNftIdeaHash !== chainedIdea.ideaSha512Hash) { return `The chainIdeas hashes does not matches the past ones.` } // This is happening two times. This was a duplication of the price validation happening // inside validateCloseIdeaWithinSelf // // validate pricing // const priceValidationResult = // await CONTRACT_TOOLS.validatePricingSignature( // idea, // cachedStrategyState, // ) // if (!priceValidationResult.valid) { // return `The pricing signatures are not valid.` // } } // now we can trust the chained ideas, we can include them in the state // and mark them as no more encrypted for (const chainedIdeaIndex in idea.chainIdeas) { const chainedIdea = idea.chainIdeas[+chainedIdeaIndex] encryptedIdeas[+chainedIdeaIndex].idea = chainedIdea.idea cachedStrategyState.encryptedIdeas.delete( encryptedIdeas[+chainedIdeaIndex].nftId!, ) } cachedStrategyState.encryptedIdeasByKey.delete( ideaComplete.content.ideaKey!, ) cachedStrategyState.orderedEncryptedIdeaKeys.splice( cachedStrategyState.orderedEncryptedIdeaKeys.indexOf( ideaComplete.content.ideaKey!, ), 1, ) // if it was th first idea ever, we check strategy timestamp if (encryptedIdeas[0].nftId === cachedStrategyState.orderedIdeasNft[0]) { if ( (encryptedIdeas[0].idea as CONTRACT_INTERFACES.ITradeIdeaIdea) .priceInfo!.price!.timestamp !== cachedStrategyState.strategy.createdAt ) { return `The first idea timestamp does not match the strategy timestamp.` } } let violation = await validateCloseIdeaWithinSelf( [...encryptedIdeas, ideaComplete], cachedStrategyState, ) if (violation === undefined) { // We set last validated nft id setLastValidatedNftId(cachedStrategyState) } return violation } const validateProccessIdeaWithinChain = ( ideaComplete: CONTRACT_INTERFACES.ITradeIdea, cachedStrategyState: CONTRACT_INTERFACES.IStrategyState, preMint: boolean = false, ): string | undefined => { // console.log('cachedStrategyState.orderedIdeasNft') // console.log(cachedStrategyState.orderedIdeasNft) let valids = cachedStrategyState.orderedIdeasNft .filter((ones) => !cachedStrategyState.nullifiedStagesNft.has(ones)) .filter((ones) => !cachedStrategyState.encryptedIdeas.has(ones)) .map((each) => cachedStrategyState.ideas.get(each)!) cachedStrategyState.allocationMap = new Map<string, number>() cachedStrategyState.totalAllocated = 0 valids = [...valids, ideaComplete] // console.log('========================') // console.log('========================') // console.log('debug: validateProccessIdeaWithinChain') // console.log('========================') // console.log('========================') // console.log('valids') // console.log(valids) const tickers = new Map<string, number>() // const tickersNftId = new Map<string, number>() for (const valid of valids) { const idea = valid.idea as CONTRACT_INTERFACES.ITradeIdeaIdea if (idea.kind === 'open') { if (tickers.has(idea.asset.ticker)) { // console.log('---------------------') // console.log('---------------------') // console.log('---------------------') // console.log('The idea causing issues:') // console.log('valid') // console.log(valid) // console.log('idea') // console.log(idea) // console.log('idea.asset') // console.log(idea.asset) // console.log('---------------------') // console.log('---------------------') // console.log('the previous idea conflict:') // const nftId = tickersNftId.get(idea.asset.ticker)! // console.log('nftId') // console.log(nftId) // const ideaV = cachedStrategyState.ideas.get(nftId) // console.log('ideaV') // console.log(ideaV) // const ideaI = ideaV?.idea // console.log('ideaI') // console.log(ideaI) // const ideaA = (ideaI as CONTRACT_INTERFACES.ITradeIdeaIdea) // ?.asset // console.log('ideaA') // console.log(ideaA) // const insideValids = valids.find((one) => one.nftId === nftId) // console.log('insideValids') // console.log(insideValids) // console.log('--') // console.log(insideValids?.idea) // console.log('--') // console.log( // (insideValids?.idea as CONTRACT_INTERFACES.ITradeIdeaIdea) // ?.asset, // ) // throw new Error('stop') return `The ticker ${idea.asset.ticker} is duplicated in the strategy.` } else { tickers.set(idea.asset.ticker, valid.content.ideaKey!) // tickersNftId.set(idea.asset.ticker, valid.nftId!) } if (cachedStrategyState.strategy.ordersType === 'ALLOCATION') { if (idea.trade?.allocation === undefined) { return `The ticker new open ALLOCATION idea requires trade allocation.` } if (idea.trade.allocation <= 0 || idea.trade.allocation > 100) { return `The ticker new open ALLOCATION idea requires trade allocation between 1 and 100.` } if (idea.trade.conviction !== undefined) { return `The ticker new open ALLOCATION idea requires trade conviction to be undefined.` } // if (!preMint) { // Adjust allocation after this idea cachedStrategyState.allocationMap.set( idea.asset.ticker, 0 + idea.trade.allocation, ) cachedStrategyState.totalAllocated! = +BigNumber( cachedStrategyState.totalAllocated!, ) .plus(idea.trade.allocation) .toFixed(2) // } // check total allocation if (cachedStrategyState.totalAllocated! > 100) { return `Idea Not Allowed: With this idea, the allocation would be greater than 100.` } // update strategy // if (!preMint) { const portfolio: { [key: string]: number } = {} for (const [ ticker, allocation, ] of cachedStrategyState.allocationMap.entries()) { portfolio[ticker] = 0 + allocation } cachedStrategyState.strategy.portfolio = portfolio cachedStrategyState.strategy.portfolioAllocated = cachedStrategyState.totalAllocated // } } // Make sure if it was the first idea, it has the matching strategy date if (!preMint && !cachedStrategyState.firstIdeaDateChecked) { if (ideaComplete.content.ideaKey === undefined) { throw new Error('ideaComplete.content.ideaKey is undefined') } if (ideaComplete.content.ideaKey === 0) { if (idea.priceInfo?.price?.timestamp === undefined) { return `The first idea timestamp is undefined.` } if (cachedStrategyState.strategy.createdAt === undefined) { return `The strategy timestamp is undefined.` } if ( idea.priceInfo!.price!.timestamp !== cachedStrategyState.strategy.createdAt ) { return `The first idea timestamp does not match the strategy timestamp.` } cachedStrategyState.firstIdeaDateChecked = true } } } else if (idea.kind === 'adjust') { // console.log('idea.asset.ticker') // console.log(idea.asset.ticker) // console.log('tickers') // console.log(tickers) if (tickers.get(idea.asset.ticker) === undefined) { return `The ticker ${idea.asset.ticker} is not defined in the strategy.` } if (tickers.get(idea.asset.ticker) !== valid.content.ideaKey!) { return `The ticker ${idea.asset.ticker} is not defined in the same stage.` } if (cachedStrategyState.strategy.ordersType === 'ALLOCATION') { // Adjust allocation after this idea const previousAllocation = cachedStrategyState.allocationMap!.get(idea.asset.ticker)! if (idea.adjustment!.kind === 'increase') { const newAllocation = +BigNumber(previousAllocation) .plus( BigNumber(previousAllocation).times( BigNumber( idea.adjustment!.percentage, ).dividedBy(100), ), ) .toFixed(2) // fix newAllocation max decimals to max between the adjustment and the previous allocation // const previousAllocationDecimals = // previousAllocation.toString().split('.')[1]?.length || 0 // const adjustmentDecimals = // idea.adjustment!.percentage.toString().split('.')[1] // ?.length || 0 // const maxDecimals = Math.max( // previousAllocationDecimals, // adjustmentDecimals, // ) // newAllocation = parseFloat( // newAllocation.toFixed(maxDecimals), // ) // make sure newAllocation is not greater then 100 if (newAllocation > 100) { return `Idea Not Allowed: With this idea, the allocation would be greater than 100.` } cachedStrategyState.allocationMap!.set( idea.asset.ticker, newAllocation, ) // adjust total allocation cachedStrategyState.totalAllocated! = +BigNumber( cachedStrategyState.totalAllocated!, ) .plus( BigNumber(newAllocation).minus(previousAllocation), ) .toFixed(2) } else { const newAllocation = +BigNumber(previousAllocation) .minus( BigNumber(previousAllocation).times( BigNumber( idea.adjustment!.percentage, ).dividedBy(100), ), ) .toFixed(2) // fix newAllocation max decimals to max between the adjustment and the previous allocation // const previousAllocationDecimals = // previousAllocation.toString().split('.')[1]?.length || 0 // const adjustmentDecimals = // idea.adjustment!.percentage.toString().split('.')[1] // ?.length || 0 // const maxDecimals = Math.max( // previousAllocationDecimals, // adjustmentDecimals, // ) // newAllocation = parseFloat( // newAllocation.toFixed(maxDecimals), // ) // newAllocation = parseFloat(newAllocation.toFixed(2)) // make sure newAllocation is not zero if (newAllocation <= 0) { return `Idea Not Allowed: With this idea, the allocation would be zero.` } cachedStrategyState.allocationMap!.set( idea.asset.ticker, newAllocation, ) // adjust total allocation cachedStrategyState.totalAllocated! = +BigNumber( cachedStrategyState.totalAllocated!, ) .plus( BigNumber(newAllocation).minus(previousAllocation), ) .toFixed(2) } // update strategy const portfolio: { [key: string]: number } = {} for (const [ ticker, allocation, ] of cachedStrategyState.allocationMap!.entries()) { portfolio[ticker] = 0 + allocation } cachedStrategyState.strategy.portfolio = portfolio cachedStrategyState.strategy.portfolioAllocated = cachedStrategyState.totalAllocated // check total allocation if (cachedStrategyState.totalAllocated! > 100) { return `Idea Not Allowed: With this idea, the allocation would be greater than 100.` } } } else { if (tickers.get(idea.asset.ticker) === undefined) { return `The ticker ${idea.asset.ticker} is not defined in the strategy.` } if (tickers.get(idea.asset.ticker) !== valid.content.ideaKey!) { return `The ticker ${idea.asset.ticker} is not defined in the same stage.` } if (cachedStrategyState.strategy.ordersType === 'ALLOCATION') { // Adjust allocation after this idea const previousAllocation = cachedStrategyState.allocationMap!.get(idea.asset.ticker)! cachedStrategyState.totalAllocated! = +BigNumber( cachedStrategyState.totalAllocated!, ) .minus(previousAllocation) .toFixed(2) cachedStrategyState.allocationMap!.delete(idea.asset.ticker)! // check total allocation if (cachedStrategyState.totalAllocated! > 100) { return `Idea Not Allowed: With this idea, the allocation would be greater than 100.` } // update strategy const portfolio: { [key: string]: number } = {} for (const [ ticker, allocation, ] of cachedStrategyState.allocationMap!.entries()) { portfolio[ticker] = 0 + allocation } cachedStrategyState.strategy.portfolio = portfolio cachedStrategyState.strategy.portfolioAllocated = cachedStrategyState.totalAllocated } const openData = cachedStrategyState.ideas.get( cachedStrategyState.validatedIdeasByKey.get( tickers.get(idea.asset.ticker)!, )![0], )! // include trade idea.trade = { direction: (openData.idea as CONTRACT_INTERFACES.ITradeIdeaIdea) .trade!.direction, conviction: ( openData.idea as CONTRACT_INTERFACES.ITradeIdeaIdea ).trade?.conviction, allocation: ( openData.idea as CONTRACT_INTERFACES.ITradeIdeaIdea ).trade?.allocation, } if (cachedStrategyState.strategy.ordersType === 'ALLOCATION') { idea.trade!.allocation = 0 } tickers.delete(idea.asset.ticker) // tickersNftId.delete(idea.asset.ticker) } } } // Make sure idea access wallets list, if there, has the creator wallet const validateWalletsAccessBasic = ( ideaComplete: CONTRACT_INTERFACES.ITradeIdea, preMint: boolean = false, ): string | undefined => { if (ideaComplete.access !== undefined) { if ( ideaComplete.access.wallets.find( (one) => one === ideaComplete.creator.walletAddress, ) === undefined ) { return `The creator wallet ${ideaComplete.creator.walletAddress} is not in the idea access wallets.` } if (!preMint) { if (ideaComplete.access.encryption === undefined) { if (typeof ideaComplete.idea === 'string') { return `The idea access encryption is undefined when expected to be filled.` } else { if ( ( ideaComplete.idea as CONTRACT_INTERFACES.ITradeIdeaIdea ).kind !== 'close' ) { return `The idea access encryption is undefined when expected to be filled.` } } } else { if (ideaComplete.access.encryption.key === undefined) { return `The idea access encryption key is undefined when expected to be filled.` } else { if (ideaComplete.access.encryption.key.length === 0) { return `The idea access encryption key is empty when expected to be filled.` } } } } } } // Make sure all ideas access wallets in previous ideas are kept in this idea const validateWalletsAccessKept = ( ideaComplete: CONTRACT_INTERFACES.ITradeIdea, cachedStrategyState: CONTRACT_INTERFACES.IStrategyState, preMint: boolean = false, ): string | undefined => { // aquire past ideas which should contain same ideaKey and ordered const pastIdeas: CONTRACT_INTERFACES.ITradeIdea[] = CONTRACT_TOOLS.queryChain.getAllIdeasByKey( ideaComplete.content.ideaKey!, cachedStrategyState, ) if (!preMint) { pastIdeas.push(ideaComplete) } let pastWallets: string[] | undefined = pastIdeas[0]?.access?.wallets || ideaComplete.access?.wallets for (const pastIdeaIndex in pastIdeas) { if (+pastIdeaIndex === 0) { const pastIdea = pastIdeas[+pastIdeaIndex] const currentWallets = pastIdea.access?.wallets if (currentWallets !== undefined) { if (pastWallets === undefined) { return `The wallet ${currentWallets} is not in the past idea access wallets.` } } else { if (pastWallets !== undefined) { return `The wallet ${pastWallets} is not in the current idea access wallets.` } } if (pastWallets !== undefined) { // make sure all past wallets are in current wallets. for (const pastWallet of pastWallets) { if (!currentWallets!.includes(pastWallet)) { return `The wallet ${pastWallet} is not in the current idea access wallets.` } } // add current wallets to past wallets for (const currentWallet of currentWallets!) { if (!pastWallets.includes(currentWallet)) { pastWallets.push(currentWallet) } } } } } if (preMint) { if (pastWallets !== undefined) { if (ideaComplete.access?.wallets === undefined) { ideaComplete.access = { wallets: [], } } const currentWallets = ideaComplete.access.wallets // make sure all past wallets are in current wallets. instead of returning an error, it will adds it to current for (const pastWallet of pastWallets) { if (!currentWallets.includes(pastWallet)) { currentWallets.push(pastWallet) } } } else { if (ideaComplete.access?.wallets !== undefined) { delete ideaComplete.access } } } } const validateProccessIdeaWithinSelf = async ( ideaComplete: CONTRACT_INTERFACES.ITradeIdea, ideaIsStringEncrypted: boolean, cachedStrategyState: CONTRACT_INTERFACES.IStrategyState, preMint: boolean = false, ): Promise<string | undefined> => { // console.log('::::::::::::::::::::::::::::::::::') // console.log('::::::::::::::::::::::::::::::::::') // console.log('::::::::::::::::::::::::::::::::::') // console.log('overall Idea Complete') // console.log(ideaComplete) if (!ideaIsStringEncrypted) { includeIdea(ideaComplete, cachedStrategyState) const idea = ideaComplete.idea as CONTRACT_INTERFACES.ITradeIdeaIdea // if it is a close idea, we check it it is a closure of past private ideas if (idea.kind === 'close') { // we check it the close ideas is a closure of a past public know idea // else // search in state for encrypted by this idea key if ( cachedStrategyState.encryptedIdeasByKey.has( ideaComplete.content.ideaKey!, ) ) { const violation = await validateProccessCloseDecryptIdea( ideaComplete, cachedStrategyState, ) if (violation) return violation /* if (violation !== undefined) { if (preMint) { // return violation return violation } else { // nullify the idea ideaComplete.nullified = violation cachedStrategyState.nullifiedStagesNft.add( ideaComplete.nftId!, ) cachedStrategyState.notNullIdeasByKey.delete( ideaComplete.content.ideaKey!, ) // We set last validated nft id setLastValidatedNftId(cachedStrategyState) } } */ } else { // in this case we do validate the partial closed idea const violation = await validateProccessPublicIdea( ideaComplete, cachedStrategyState, ) if (violation) return violation /* if (violation) { ideaComplete.nullified = violation cachedStrategyState.nullifiedStagesNft.add( ideaComplete.nftId!, ) cachedStrategyState.notNullIdeasByKey.delete( ideaComplete.content.ideaKey!, ) setLastValidatedNftId(cachedStrategyState) } */ } } else { // if it is not a close idea, it is a public idea stage // in this case we do validate the partial open/adjust stage const violation = await validateProccessPublicIdea( ideaComplete, cachedStrategyState, ) if (violation) { return violation } } } else { if (!preMint || ideaComplete.nftId !== undefined) { let violation: string | undefined = validateBasicEncryptedIdea( ideaComplete, cachedStrategyState, preMint, ) if (violation) return violation violation = await validateProccessEncryptedIdea( ideaComplete, cachedStrategyState, ) if (violation) return violation includeEncryptedIdea(ideaComplete, cachedStrategyState) } } // if (ideaComplete.nullified === undefined) { let violation: string | undefined = validateWalletsAccessBasic( ideaComplete, preMint, ) if (violation) return violation violation = validateWalletsAccessKept( ideaComplete, cachedStrategyState, preMint, ) if (violation) return violation // } return undefined } /* TODO: Efficiency path: shrinking: URGENT => Make sure reuse cache everywhere! (checking) (1) Observe useless data and try to create this roadpath: (DONE!) (...) (2) We IPFSID-FY images and large files in the nft data PRIOR minting! (and access ipfsId before showing)! (DONE) ToIpfsIdFy => ideaComplete.openSeaIdea.idea.public.image => ipfsId-fy ideaComplete.openSeaIdea.image => eliminate (3) After all idea proccess, prior return cache, remove all unused data that won't be used in the future validation anymore. (will do next maybe if still very big) attention on the ideas encrypted that will be revisited by validation function after close and made public effect 2: Search for functions outside this that uses this cache if they make use of any idea data you are about to delete first. e2a: in case it does: try alternatives Issued => getStrategyMetadata // src/modules/activ-v4/modules/chunks/activ-v4.module.ts => depends on the new queries complete e2b: keep it =>> POSTPONED! (5) Prior reading cache and After generating cache => Unzip-ish it/ Zip-ish it! => (will do next maybe if still very big) Zip function that will: a) exchange variables names by letters and small texts. b) shrink/compact text values with ready to use on market packages made for this purpose. (4) POSTPONED => Zip images and large files in the nft data PRIOR minting! (and unzip before showing) => POSTPONED */ export const rules = async ( strategyIdeas: CONTRACT_INTERFACES.ITradeIdea[], cachedStrategyState?: CONTRACT_INTERFACES.IStrategyState, preMint: boolean = false, ): Promise<CONTRACT_INTERFACES.IRuleValidResult | string> => { // console.log('strategyIdeas[strategyIdeas.length - 2]') // console.log(strategyIdeas[strategyIdeas.length - 2]) // console.log('strategyIdeas[strategyIdeas.length - 1]') // console.log(strategyIdeas[strategyIdeas.length - 1]) // if ( // strategyIdeas[strategyIdeas.length - 1].nftId === // strategyIdeas[strategyIdeas.length - 2].nftId // ) { // } if (strategyIdeas.length === 0) { return 'Void Strategy' } const strategyIdeasChronological = sortIdeasChronological(strategyIdeas) const firstIdea = strategyIdeasChronological[0] if (firstIdea.strategy.ordersType === undefined) { firstIdea.strategy.ordersType = 'STANDARD' } if (cachedStrategyState === undefined) { cachedStrategyState = getInitialStrategyState(firstIdea) } // if ( // strategyIdeas[0].strategy.uniqueKey === // 'nAqta5S9H5M77y7pnmpeQqfxwpgJeiUsEYeJsMojG1UazoaauSKUhqDqH9fNdmqcJHZvm1eFogaatJBzojzSDB5cKci6CgxBwEEiSXT1xyawNBtDuddehhMh3ktLgWbyfpLtgoM49YevpVkyAMsjmmfwNVK8jUKcv' // ) { // console.log('&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&') // console.log('strategyIdeas[0].strategy') // console.log(strategyIdeas[0].strategy) // console.log('strategyIdeas[0].strategy.reference') // console.log(strategyIdeas[0].strategy.reference) // console.log('strategyIdeas[0].strategy.uniqueKey') // console.log(strategyIdeas[0].strategy.uniqueKey) // console.log('strategyIdeas') // console.log(strategyIdeas) // console.log('cachedStrategyState') // console.log(cachedStrategyState) // console.log('strategyIdeasChronological[0]') // console.log(strategyIdeasChronological[0]) // } // console.log('strategyIdeasChronological[0].nftId') // console.log(strategyIdeasChronological[0].nftId) // console.log('cachedStrategyState.lastNftId') // console.log(cachedStrategyState.lastNftId) // console.log('&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&') if (strategyIdeasChronological[0].nftId !== undefined) { if ( strategyIdeasChronological[0].nftId === cachedStrategyState.lastNftId ) { // we remove this idea strategyIdeasChronological.shift() } } // console.log('strategyIdeasChronological after shift') // console.log(strategyIdeasChronological) let violation: string | undefined = undefined for (const idea of strategyIdeasChronological) { // console.log('idea') // console.log(idea) // const onlyValidate = idea.nftId === undefined // console.log('preMint') // console.log(preMint) // console.log('idea.nftId') // console.log(idea.nftId) if ( (!preMint || idea.nftId !== undefined) && idea.nullified === undefined ) { if (idea.nftId === undefined) { return 'NFT ID is required' } if (idea.nftId! < cachedStrategyState.lastNftId) { return 'NFT ID must be greater than last NFT ID' } } if (idea.nullified !== undefined && !preMint) { cachedStrategyState.nullifiedStagesNft.add(idea.nftId!) } if (idea.nullified === undefined) { // bellow is not true if idea is nullified due to error in middle of creation const ideaIsStringEncrypted = typeof idea.idea === 'string' if ( idea.access?.encryption?.encrypt !== true && ideaIsStringEncrypted ) { return 'If the idea is string, it should figure as encrypted.' } // console.log('ideaIsStringEncrypted:') // console.log(ideaIsStringEncrypted) violation = await validateProccessIdeaWithinSelf( idea, ideaIsStringEncrypted, cachedStrategyState, preMint, ) if (violation) { return violation } if (!ideaIsStringEncrypted && idea.nullified === undefined) { violation = validateProccessIdeaWithinChain( idea, cachedStrategyState, preMint, ) if (violation) { /* if (preMint) { // return violation return violation } else { // nullify the idea idea.nullified = violation cachedStrategyState.nullifiedStagesNft.add(idea.nftId!) cachedStrategyState.notNullIdeasByKey.delete( idea.content.ideaKey!, ) setLastValidatedNftId(cachedStrategyState) } */ return violation } } if (!preMint || idea.nftId !== undefined) { // console.log('adding to ordered nfts') // console.log('idea') // console.log(idea) // if (idea.nullified === undefined) { cachedStrategyState.orderedIdeasNft.push(idea.nftId!) // } cachedStrategyState.lastNftId = idea.nftId! // console.log('cachedStrategyState.orderedIdeasNft') // console.log(cachedStrategyState.orderedIdeasNft) // console.log('cachedStrategyState.lastNftId') // console.log(cachedStrategyState.lastNftId) } } } /** **/ return { valid: true, cachedStrategyState, } }