@ixily/activ
Version:
Alpha Capture Trade Idea Verification. Blockchain ownership proven trade ideas and strategies.
1,085 lines (1,027 loc) • 35.4 kB
text/typescript
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,
}
}