@ixily/activ
Version:
Alpha Capture Trade Idea Verification. Blockchain ownership proven trade ideas and strategies.
387 lines (367 loc) • 11.5 kB
text/typescript
import {
getDataWithPaging,
IContract,
IPaging,
isNullOrUndefined,
LitModule,
LogModule as log,
NftModule,
NftStoreModule,
CONTRACT_INTERFACES,
ContractModule,
} from '../..'
const restoreImage = async (cid: string): Promise<string> => {
return NftStoreModule.getJsonContent(cid)
}
const restoreImages = async (idea: CONTRACT_INTERFACES.ITradeIdea) => {
// Process related to the NFT image
const p1 = async () => {
if (idea.idea !== undefined) {
if (typeof idea.idea !== 'string') {
if (idea.idea.asset.image?.cid !== undefined) {
if (idea.idea.asset.image.b64 === undefined) {
const p1Img = await restoreImage(
idea.idea.asset.image.cid,
)
// console.log('=================================')
// console.log('[P1] restoreImages (imageIsCid)', idea.idea!.public.imageIsCid)
// console.log('[P1] restoreImages (ipfs id)', idea.idea!.public.image)
// console.log('[P1] restoreImages (img)', p1Img)
// console.log('=================================')
idea.idea.asset.image.b64 = p1Img
}
}
}
}
}
// Process related to the strategy image
const p2 = async () => {
if (idea.strategy.image?.cid !== undefined) {
if (idea.strategy.image?.b64 === undefined) {
const p2Img = await restoreImage(idea.strategy.image.cid)
// console.log('=================================')
// console.log('[P2] restoreImages (imageIsCid)', idea.idea!.public.imageIsCid)
// console.log('[P2] restoreImages (ipfs id)', idea.idea!.public.image)
// console.log('[P2] restoreImages (img)', p2Img)
// console.log('=================================')
idea.strategy.image.b64 = p2Img
}
}
}
// Process related to the user company image
const p3 = async () => {
if (idea.creator.companyLogo?.cid !== undefined) {
if (idea.creator.companyLogo.b64 === undefined) {
const p3Img = await restoreImage(idea.creator.companyLogo?.cid)
// console.log('=================================')
// console.log('[P3] restoreImages (imageIsCid)', idea.idea!.public.imageIsCid)
// console.log('[P3] restoreImages (ipfs id)', idea.idea!.public.image)
// console.log('[P3] restoreImages (img)', p3Img)
// console.log('=================================')
idea.creator.companyLogo.b64 = p3Img
}
}
}
await Promise.all([p1(), p2(), p3()])
}
const canDecrypt = async (
idea: CONTRACT_INTERFACES.ITradeIdea,
contract?: IContract,
creator?: string,
): Promise<boolean> => {
contract = ContractModule.thisOrGet(contract)
creator = await ContractModule.thisOrGetCreator(creator, contract)
if (idea.creator.walletAddress === creator) {
return true
}
if (idea.access !== undefined) {
if (idea.access.wallets.includes(creator)) {
return true
}
}
return false
}
const getIdeaByNftId = async (
ideaNft: number,
contract: IContract,
decrypt: boolean = true,
inflateImages: boolean = false,
): Promise<CONTRACT_INTERFACES.ITradeIdea> => {
// log.dev('getIdeaByNftId (ideaNft)', ideaNft)
const nftMidUrl = await NftModule.view.general.getIdeaMetadataUriByNftId(
ideaNft,
contract,
)
// log.dev('getIdeaByNftId (nftMidUrl)', nftMidUrl)
// log.dev('getting from nft store:')
const ideaString = await NftStoreModule.getJsonContent(nftMidUrl)
// log.dev('getIdeaByNftId (ideaString)', ideaString)
const parsedIdea = JSON.parse(ideaString) as CONTRACT_INTERFACES.ITradeIdea
parsedIdea.nftId = ideaNft
parsedIdea.url = nftMidUrl
if (decrypt) {
if (await canDecrypt(parsedIdea, contract)) {
return restoreIdeaByNftId(parsedIdea, inflateImages)
}
}
// TODO: Add block timestamp
return parsedIdea
}
const authorizedToDecrypt = (
tradeIdea: CONTRACT_INTERFACES.ITradeIdea,
me: string,
): boolean => {
if (tradeIdea.creator.walletAddress === me) {
return true
} else {
if (tradeIdea.access !== undefined) {
if (tradeIdea.access.wallets.includes(me)) {
return true
} else {
return false
}
} else {
return true
}
}
}
const restoreIdeaByNftId = async (
tradeIdea: CONTRACT_INTERFACES.ITradeIdea,
inflateImages: boolean = true,
): Promise<CONTRACT_INTERFACES.ITradeIdea> => {
// console.log('¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨')
// console.log('tradeIdea:')
// console.log(tradeIdea)
// console.log('tradeIdea.access?.encryption')
// console.log(tradeIdea.access?.encryption)
if (
tradeIdea.access?.encryption?.key !== undefined &&
tradeIdea.idea !== '' &&
typeof tradeIdea.idea === 'string' &&
tradeIdea.nullified === undefined
) {
const isPublic = tradeIdea.isPublic || false
// console.log('isPublic')
// console.log(isPublic)
let restoredStringIdea: string | undefined = undefined
const me = await ContractModule.thisOrGetCreator(undefined)
if (authorizedToDecrypt(tradeIdea, me)) {
// console.log('tradeIdea')
// console.log(tradeIdea)
// if (tradeIdea.nftId === undefined) {
// throw new Error('check')
// }
// if it is 'FAILED_IN_THE_MIDDLE_OF_CREATION', give it one extra try after one minute to confirm it is not temporary due to middle of creation
const middleOfCreationMaxRetries = 3
const middleOfCreationDelays = [20, 40, 100]
let middleOfCreationRetry = 0
while (
middleOfCreationRetry <= middleOfCreationMaxRetries &&
(restoredStringIdea === undefined ||
restoredStringIdea === 'FAILED_IN_THE_MIDDLE_OF_CREATION')
) {
// debug used to simulate error scenario
// restoredStringIdea = 'FAILED_IN_THE_MIDDLE_OF_CREATION'
restoredStringIdea =
await LitModule.recoverPublicIdeaKeyFromLitForNftId(
tradeIdea.idea,
tradeIdea.access.encryption.key,
tradeIdea.nftId!,
isPublic,
).catch((err) => {
log.prod(
'restoreIdeaByNftId (error from lit operation): ',
err?.message || 'no message',
)
if (
err.message.indexOf(
'The access control condition check failed. Are you sure you meet the conditions?',
) !== -1
) {
return 'NOT_ACCESS_TO_IDEA'
}
if (
err.message.indexOf(
'The access control conditions you passed in do not match the ones that were set by the condition creator for this encryptedSymmetricKey.',
) !== -1
) {
return 'FAILED_IN_THE_MIDDLE_OF_CREATION'
}
throw err
})
if (restoredStringIdea === 'FAILED_IN_THE_MIDDLE_OF_CREATION') {
if (middleOfCreationRetry < middleOfCreationMaxRetries) {
await new Promise((resolve) => {
setTimeout(
resolve,
middleOfCreationDelays[middleOfCreationRetry],
)
})
}
middleOfCreationRetry++
}
}
} else {
// still try to decrypt to get to know if I am only unauthorized or it is a case of 'FAILED_IN_THE_MIDDLE_OF_CREATION'
const middleOfCreationMaxRetries = 3
const middleOfCreationDelays = [20, 40, 100]
let middleOfCreationRetry = 0
while (
middleOfCreationRetry <= middleOfCreationMaxRetries &&
(restoredStringIdea === undefined ||
restoredStringIdea === 'FAILED_IN_THE_MIDDLE_OF_CREATION')
) {
// debug used to simulate error scenario
// restoredStringIdea = 'FAILED_IN_THE_MIDDLE_OF_CREATION'
restoredStringIdea =
await LitModule.recoverPublicIdeaKeyFromLitForNftId(
tradeIdea.idea,
tradeIdea.access.encryption.key,
tradeIdea.nftId!,
isPublic,
).catch((err) => {
log.prod(
'Lit Recovering Key Error: ' + err.message ||
'no message' +
' at idea id: ' +
tradeIdea.nftId,
)
// Bellow is actually a temporary bug in Lit protocol servers.
// if (
// err.message.indexOf(
// 'There was an error getting the signing shares from the nodes',
// ) !== -1
// ) {
// return 'FAILED_IN_THE_MIDDLE_OF_CREATION'
// }
if (
err.message.indexOf(
'The access control condition check failed. Are you sure you meet the conditions?',
) !== -1
) {
return 'NOT_ACCESS_TO_IDEA'
}
if (
err.message.indexOf(
'The access control conditions you passed in do not match the ones that were set by the condition creator for this encryptedSymmetricKey.',
) !== -1
) {
return 'FAILED_IN_THE_MIDDLE_OF_CREATION'
}
throw err
})
if (restoredStringIdea === 'FAILED_IN_THE_MIDDLE_OF_CREATION') {
if (middleOfCreationRetry < middleOfCreationMaxRetries) {
await new Promise((resolve) => {
setTimeout(
resolve,
middleOfCreationDelays[middleOfCreationRetry],
)
})
}
middleOfCreationRetry++
}
}
}
// console.log('restoredStringIdea')
// console.log(restoredStringIdea)
if (
restoredStringIdea !== 'NOT_ACCESS_TO_IDEA' &&
restoredStringIdea !== 'FAILED_IN_THE_MIDDLE_OF_CREATION'
) {
const restoredIdea = JSON.parse(restoredStringIdea!)
tradeIdea.idea = restoredIdea as CONTRACT_INTERFACES.ITradeIdeaIdea
// log.dev('restoreIdeaByNftId (restoredIdea)', restoredIdea)
}
if (restoredStringIdea === 'FAILED_IN_THE_MIDDLE_OF_CREATION') {
tradeIdea!.nullified = 'FAILED_IN_THE_MIDDLE_OF_CREATION'
}
} else if (
typeof tradeIdea.idea === 'string' &&
tradeIdea.nullified === undefined
) {
const restoredIdea = JSON.parse(tradeIdea.idea)
tradeIdea!.idea = restoredIdea as CONTRACT_INTERFACES.ITradeIdeaIdea
}
if (inflateImages) {
await restoreImages(tradeIdea)
}
return tradeIdea
}
const getRestoredIdeaByNftId = async (
ideaNft: number,
contract: IContract,
inflateImages: boolean = true,
) => {
const tradeIdea = await getIdeaByNftId(ideaNft, contract)
// console.log('tradeIdea')
// console.log(tradeIdea)
return restoreIdeaByNftId(tradeIdea, inflateImages)
}
const getRestoredIdeasByNftIds = async (
myTokens: number[],
contract: IContract,
): Promise<CONTRACT_INTERFACES.ITradeIdea[]> => {
log.dev('myTokens:', myTokens)
return Promise.all(
myTokens.map((token) => getRestoredIdeaByNftId(token, contract)),
)
}
const getRestoredIdeasByNftIdsWithPaginationAndFilter = async (
myTokens: number[],
page: number,
limit: number,
contract: IContract,
filterIdeaKind?: CONTRACT_INTERFACES.ITradeIdeaIdeaKind,
skipError?: boolean,
): Promise<IPaging<CONTRACT_INTERFACES.ITradeIdea>> => {
const dataWithPagingIncluded = getDataWithPaging({
data: myTokens,
paging: {
page,
limit,
},
})
myTokens = dataWithPagingIncluded.data
let ownedAccessIdeas: any[] = []
if (skipError) {
ownedAccessIdeas = await Promise.all(
myTokens.map((token) => {
return (async () => {
return getRestoredIdeaByNftId(token, contract).catch(
(err) => {
return undefined
},
)
})()
}),
)
ownedAccessIdeas = ownedAccessIdeas.filter((each) => {
return each !== undefined
})
} else {
ownedAccessIdeas = await Promise.all(
myTokens.map((token) => getRestoredIdeaByNftId(token, contract)),
)
}
log.dev('restoredIdeasWithPagination (ownedAccessIdeas)', ownedAccessIdeas)
if (!isNullOrUndefined(filterIdeaKind)) {
ownedAccessIdeas = ownedAccessIdeas.filter((each) => {
return each.idea?.idea?.kind === filterIdeaKind
})
}
const response = {
...dataWithPagingIncluded,
data: ownedAccessIdeas,
}
return response
}
export const CryptoIdeasModule = {
getIdeaByNftId,
restoreIdeaByNftId,
restoreImage,
restoreImages,
getRestoredIdeaByNftId,
getRestoredIdeasByNftIds,
getRestoredIdeasByNftIdsWithPaginationAndFilter,
}