UNPKG

@ixily/activ

Version:

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

387 lines (367 loc) 11.5 kB
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, }