UNPKG

@ixily/activ

Version:

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

1,819 lines (1,676 loc) 85 kB
import { AuthSig } from '@lit-protocol/types' import axios from 'axios' import { BigNumber } from 'bignumber.js' import 'isomorphic-fetch' import { // retryFunctionHelper, CONTRACT_INTERFACES, CONTRACT_TOOLS, ContractModule, CryptoIdeasModule, CryptoJs, IContract, IFiatMintRequest, IFiatMintResult, IOperationalCost, IPaging, // IPortfolioEntry, IPortfolioAssetRequest, // IPortfolioCreationResult, // IPortfolioResultEntry, IPortfolioRequest, IPortfolioResult, IStrategyChain, IStrategyIdeaChain, IdeaCreatedEvent, IdeaCreationFailed, IdeaCreationResult, IdeaPerformanceModule, IdeaTrafficDirectorModule, ImagesModule, IxilyAssistedFiatMint, LitModule, NftModule, NftStoreModule, ProvableModule, RULES, StrategyChainModule, copyObj, dynamicCachedFunction, dynamicCachedFunctionWithCache, getUniqueStrategyReference, id, isNullOrUndefined, isNullOrWhiteSpace, LogModule as log, loop, rest, } from '../../' import { ParallelChunksModule } from './parallel-chunks.module' import CI = CONTRACT_INTERFACES const assert = (toCheck: boolean, errorMsg: string) => { if (!toCheck) { throw new Error(errorMsg) } } // type DataFilter = 'all' | 'my' | 'accesible' | 'public' const state = { contractNetworkParallelPools: [] as Array<{ contractNetwork: string contract: string network: string pool: Array<() => Promise<void>> }>, worker: undefined as any, workerLock: false as boolean, } const processImage = async ( img?: CI.ITradeIdeaImage, format: 'profile' | 'banner' = 'profile', ) => { if (img !== undefined) { const goodOne = !isNullOrWhiteSpace(img.b64) ? img.b64 : img.url if (!isNullOrWhiteSpace(goodOne)) { if (isNullOrWhiteSpace(img.cid)) { const stringBase64 = await ImagesModule.convertOrMinifyImageToBase64( goodOne, format, ) const resultStored = await NftStoreModule.storeJsonContent( stringBase64, ) img.cid = resultStored.cid } } delete img.b64 delete img.url } } const processImages = async (tradeIdea: CI.ITradeIdea) => { type PromGen = () => Promise<void> const ps: PromGen[] = [] ps.push(async () => { await processImage( (tradeIdea.idea as CI.ITradeIdeaIdea).asset.image, 'profile', ) }) ps.push(async () => { await processImage(tradeIdea.strategy.image, 'banner') }) ps.push(async () => { await processImage(tradeIdea.creator.companyLogo, 'profile') }) await Promise.all(ps.map((p) => p())) } // Query contract for all users that has access to the idea that came before the current one // and make sure this one wallets array contains all of them const addAnyPossiblyNewestUserMissingInWallet = async ( tradeIdea: CI.ITradeIdea, strategyChain: CI.IStrategyState, contract: IContract, ) => { // check first if the current idea is adjust or close const idea = tradeIdea.idea as CI.ITradeIdeaIdea if ( idea.kind === 'adjust' || (idea.kind === 'close' && tradeIdea.access !== undefined) ) { // figures previous stageKey if (tradeIdea.content.ideaKey === undefined) { throw new Error( 'PROVABLE IDEAS MODULE ERROR: tradeIdea.content.ideaKey is undefined', ) } const previousIdeas = CONTRACT_TOOLS.queryChain.getAllIdeasByKey( tradeIdea.content.ideaKey, strategyChain, ) // retrieve previous idea which is the last entry of prev array const previousIdea = previousIdeas[previousIdeas.length - 1] // query all users that has previous idea access const previousIdeaAccessWallets = await NftModule.view.general.getIdeaViewers( contract, previousIdea.nftId!, ) // adds each previousIdeaAccessWallet that is missing on tradeIdea.access.wallets previousIdeaAccessWallets.forEach((wallet) => { if (!tradeIdea.access!.wallets.includes(wallet)) { tradeIdea.access!.wallets.push(wallet) } }) } } const packIdea = async ( contract: IContract, tradeIdea: CI.ITradeIdea, creator?: string, cached?: { chainOfStrategy: IStrategyChain cachedStrategyState: CI.IStrategyState }, ): Promise<{ nftMidUrl: string nftMid: string encryptedIdea?: string encryptedPublicKey?: string encryptedSymetricKeyRaw?: string symmetricKey?: string authSig: AuthSig }> => { // console.log('tradeIdea') // console.log(tradeIdea) let encryptedPublicKey: string | undefined let encryptedSymetricKeyRaw: string | undefined let symmetricKey: string | undefined let encryptedIdea: string | undefined let authSig: any contract = ContractModule.thisOrGet(contract) creator = await ContractModule.thisOrGetCreator(creator, contract) // add creator wallet tradeIdea.creator.walletAddress = creator // tradeIdea.strategy.creatorWallet = creator // tradeIdea.creator.name = tradeIdea.creator.name tradeIdea.strategy.uniqueKey = getUniqueStrategyReference( contract.recipe.chain, contract.gate!.address, creator, tradeIdea.strategy.reference, ) // delete to assure lack of provided key delete tradeIdea.access?.encryption // delete to assure lack of idea key on create case if ((tradeIdea.idea as CI.ITradeIdeaIdea).kind === 'open') { delete tradeIdea.content.ideaKey } let chainOfStrategy: IStrategyChain let cachedStrategyState: CI.IStrategyState if (cached !== undefined) { chainOfStrategy = cached.chainOfStrategy cachedStrategyState = cached.cachedStrategyState } else { const chainOfStrategyWithCache = await IdeaTrafficDirectorModule.runSequentialTicket( [contract.gate!.address, creator, tradeIdea.strategy.reference], async () => { return getValidatedStrategyChainWithCache( tradeIdea.strategy.reference, creator, contract, false, ) }, ) chainOfStrategy = chainOfStrategyWithCache.result cachedStrategyState = chainOfStrategyWithCache.cache.cachedStrategy } // console.log('chainOfStrategy') // console.log(chainOfStrategy) // add strategy creation timestamp // throw new Error('check above') if (chainOfStrategy.ideas.length === 0) { tradeIdea.strategy.createdAt = ( tradeIdea.idea as CI.ITradeIdeaIdea ).priceInfo!.price!.timestamp } const chronological = StrategyChainModule.chronologicalIdeasFromChain(chainOfStrategy) tradeIdea.creator.walletAddress = creator const tradeIdeaIdea = tradeIdea.idea as CI.ITradeIdeaIdea // Make sure creator is also owner of the idea if (tradeIdea.access !== undefined) { if (!tradeIdea.access.wallets.includes(creator)) { tradeIdea.access.wallets.push(creator) } } if (tradeIdeaIdea.kind === 'open') { // protect against wrongly passed idea key delete tradeIdea.content.ideaKey // OPEN IDEA // deal ideaChain await processImages(tradeIdea) const deterministicIdeaString = CONTRACT_TOOLS.deterministicStringify( tradeIdea.idea, ) as string if (deterministicIdeaString === undefined) { throw new Error( 'PROVABLE IDEAS MODULE ERROR: deterministicIdeaString is undefined', ) } tradeIdea.ideaSha512Hash = CryptoJs.SHA512( deterministicIdeaString, ).toString() log.dev('tradeIdea.ideaSha512Hash:') log.dev(tradeIdea.ideaSha512Hash) // console.log('chronological') // console.log(chronological) // console.log('tradeIdea') // console.log(tradeIdea) tradeIdeaIdea.chainIdeas = [] const validation = await RULES.v1.rules( [tradeIdea], copyObj(cachedStrategyState), true, ) if (typeof validation === 'string') { throw new Error(('Invalid idea: ' + validation) as string) } // tradeIdea.strategy = validation.cachedStrategyState!.strategy if (tradeIdea.access !== undefined) { const stored = await LitModule.storeTemporaryPublicIdeaKeyOnLit( deterministicIdeaString, tradeIdea.isPublic, ) encryptedPublicKey = stored.encryptedPublicKey encryptedSymetricKeyRaw = stored.encryptedSymetricKeyRaw symmetricKey = stored.symmetricKey encryptedIdea = stored.encryptedIdea authSig = stored.authSig // log.dev('encryptedIdea:') // log.dev(encryptedIdea) tradeIdea.access.encryption = { encrypt: true, key: encryptedPublicKey, } tradeIdea.idea = encryptedIdea } } else if (tradeIdeaIdea.kind === 'adjust') { // ADJUST IDEA // First: We must now include the past stages of the idea // console.log('chronological') // console.log(chronological) // throw new Error('check above') const pastIdeas = chronological.filter( (ones) => ones.content.ideaKey === tradeIdea.content.ideaKey, ) log.dev('pastIdeas:') log.dev(pastIdeas) // throw new Error('check above') const chainIdeas: CI.IChainIdea[] = [] for (const pastIdea of pastIdeas) { let nftId = null if ((pastIdea?.nftId as any)?.type === 'BigNumber') { nftId = Number((pastIdea?.nftId as any)?.hex) } else { nftId = Number(pastIdea?.nftId) } const pastIdeaIdea = pastIdea.idea as CI.ITradeIdeaIdea const pastIdeaHash = pastIdea.ideaSha512Hash! const chainIdea: CI.IChainIdea = { nftId, reference: pastIdea.content.reference, clientTokenId: pastIdea.content.clientTokenId, ideaStageKey: pastIdea.content.ideaStageKey, ideaKey: pastIdea.content.ideaKey, url: pastIdea.url as string, idea: pastIdeaIdea, ideaSha512Hash: pastIdeaHash, } chainIdeas.push(chainIdea) } tradeIdeaIdea.chainIdeas = chainIdeas // Second: We must calculate the idea performance const ideaPerformance = IdeaPerformanceModule.getIdeaPerformance([ ...pastIdeas, tradeIdea, ]) // log.dev('ideaPerformance:') // log.dev(ideaPerformance) ;(tradeIdea.idea as CI.ITradeIdeaIdea).result = ideaPerformance await processImages(tradeIdea) const deterministicIdeaString = CONTRACT_TOOLS.deterministicStringify( tradeIdea.idea, ) as string if (deterministicIdeaString === undefined) { throw new Error( 'PROVABLE IDEAS MODULE ERROR: deterministicIdeaString is undefined', ) } tradeIdea.ideaSha512Hash = CryptoJs.SHA512( deterministicIdeaString, ).toString() // Third: We must check if the idea is valid const validation = await RULES.v1.rules( [tradeIdea], copyObj(cachedStrategyState), true, ) if (typeof validation === 'string') { throw new Error(('Invalid idea: ' + validation) as string) } // const validationAgain = await rules(chronologicalIdeas, undefined, true) // if (typeof validationAgain === 'string') { // throw new Error(('Invalid idea: ' + valid.validation) as string) // } // tradeIdea.strategy = validation.cachedStrategyState.strategy // Fifth: We must encrypt the idea if applicable if (tradeIdea.access !== undefined) { await addAnyPossiblyNewestUserMissingInWallet( tradeIdea, cachedStrategyState, contract, ) const stored = await LitModule.storeTemporaryPublicIdeaKeyOnLit( deterministicIdeaString, tradeIdea.isPublic, ) encryptedPublicKey = stored.encryptedPublicKey encryptedSymetricKeyRaw = stored.encryptedSymetricKeyRaw symmetricKey = stored.symmetricKey encryptedIdea = stored.encryptedIdea authSig = stored.authSig // log.dev('encryptedIdea:') // log.dev(encryptedIdea) tradeIdea.access.encryption = { encrypt: true, key: encryptedPublicKey, } tradeIdea.idea = encryptedIdea } // log.dev('tradeIdea.ideaSha512Hash:') // log.dev(tradeIdea.ideaSha512Hash) } else { // CLOSE IDEA // First: We must now open to public the past stages of the idea const pastIdeas = chronological.filter( (ones) => ones.content.ideaKey === tradeIdea.content.ideaKey, ) // log.dev('pastIdeas:') // log.dev(pastIdeas) const chainIdeas: CI.IChainIdea[] = [] for (const pastIdea of pastIdeas) { let nftId = null if ((pastIdea?.nftId as any)?.type === 'BigNumber') { nftId = Number((pastIdea?.nftId as any)?.hex) } else { nftId = Number(pastIdea?.nftId) } const pastIdeaIdea = pastIdea.idea as CI.ITradeIdeaIdea const pastIdeaHash = pastIdea.ideaSha512Hash! const chainIdea: CI.IChainIdea = { nftId, reference: pastIdea.content.reference, clientTokenId: pastIdea.content.clientTokenId, ideaStageKey: pastIdea.content.ideaStageKey, ideaKey: pastIdea.content.ideaKey, url: pastIdea.url as string, idea: pastIdeaIdea, ideaSha512Hash: pastIdeaHash, } chainIdeas.push(chainIdea) } tradeIdeaIdea.chainIdeas = chainIdeas await processImages(tradeIdea) const deterministicIdeaString = CONTRACT_TOOLS.deterministicStringify( tradeIdea.idea, ) as string if (deterministicIdeaString === undefined) { throw new Error( 'PROVABLE IDEAS MODULE ERROR: deterministicIdeaString is undefined', ) } tradeIdea.ideaSha512Hash = CryptoJs.SHA512( deterministicIdeaString, ).toString() // Second: We must calculate the idea performance const ideaPerformance = IdeaPerformanceModule.getIdeaPerformance([ ...pastIdeas, tradeIdea, ]) // log.dev('ideaPerformance:') // log.dev(ideaPerformance) ;(tradeIdea.idea as CI.ITradeIdeaIdea).result = ideaPerformance // Third: We must check if the idea is valid again const validation = await RULES.v1.rules( [tradeIdea], copyObj(cachedStrategyState), true, ) if (typeof validation === 'string') { throw new Error(('Invalid idea: ' + validation) as string) } // tradeIdea.strategy = validation.cachedStrategyState.strategy } const stringified = CONTRACT_TOOLS.deterministicStringify(tradeIdea) if (stringified === undefined) { throw new Error( 'PROVABLE IDEAS MODULE ERROR: stringify open sea metadata failed', ) } const resultStored = await NftStoreModule.storeJsonContent(stringified) const nftMid = resultStored.cid const nftMidUrl = resultStored.url return { nftMidUrl, nftMid, encryptedIdea, encryptedPublicKey, encryptedSymetricKeyRaw, symmetricKey, authSig, } } const getPricingInfo = async ( pricing: CI.ITradeIdeaPricing, ticker: string, tracker?: string, croupierUrl?: string, ): Promise<CI.IValidPrice> => { // Special Rules for pricing providers if (pricing.provider === 'IEX') { if (pricing.credentials?.key === undefined) { throw new Error('IEX pricing provider requires an API key') } } let priceDetailsFromLitAction: CI.IValidPrice // TEMP HOTLOG // console.log('ACTIV: getPricingInfo: tradeIdea.tracker: ', tradeIdea.tracker) pricing = copyObj(pricing) priceDetailsFromLitAction = await ProvableModule.getVerifiedPriceDetails( ticker, pricing.provider, pricing.credentials, { tracker, croupierUrl, }, ) log.dev('createIdea (priceDetailsFromLitAction)', priceDetailsFromLitAction) // throw new Error('good price test breakpoint') // turn network back to goerli after goerli pricing (only when alpha dev) // const { chainId } = await contract.provider.getNetwork() // log.dev('createIdea post (preIdeaKey)', preIdeaKey) // log.dev('createIdea post (_ideaKey)', _ideaKey) // log.dev('createIdea post (tradeIdea.content.ideaKey)', tradeIdea.content.ideaKey) // log.dev('createIdea post (tradeIdea)', tradeIdea) if ((priceDetailsFromLitAction.price!.globalPrice || 0) <= 0) { throw new Error( `The price provider "${pricing.provider}" didn't find a valid price. Verify again!`, ) } // attach price details retrieved to the idea return priceDetailsFromLitAction } // const LIT_UPDT_LIT_JS_TASK = 'litupdt' // const LIT_UPDT_LIT_JS_TASK_CHUNKS = 1 // ParallelChunksModule.defineTaskChunks( // LIT_UPDT_LIT_JS_TASK, // LIT_UPDT_LIT_JS_TASK_CHUNKS, // ) const coreCreateIdea = async ( tradeIdea: CI.ITradeIdea, contract: IContract, client?: string, gasAggressivity: number = 2, ixilyAssistedFiatMint?: IxilyAssistedFiatMint, cached?: { chainOfStrategy: IStrategyChain cachedStrategyState: CI.IStrategyState }, ): Promise<IdeaCreationResult> => { log.dev('++++++++++++++++++++++++++++++++++++++++++++++++++++++++') const tracker = tradeIdea?.tracker || null log.dev('coreCreateIdea (tracker)', tracker) // log.dev('tradeIdea.idea:') // log.dev(tradeIdea.idea) // log.dev(typeof tradeIdea.idea === 'string') if (typeof tradeIdea.idea === 'string') { throw new Error( 'PROVABLE IDEAS ERROR: tradeIdea.idea must be an object', ) } const ideaPayload = tradeIdea.idea as CI.ITradeIdeaIdea log.dev('providerCreateIdea (symbol)', ideaPayload.asset.ticker) log.dev('providerCreateIdea (provider)', tradeIdea.pricing.provider) // log.dev('providerCreateIdea (auth)', tradeIdea?.pricing?.credentials) if (tradeIdea.idea.priceInfo === undefined) { tradeIdea.idea.priceInfo = await getPricingInfo( tradeIdea.pricing, tradeIdea.idea!.asset.ticker, tradeIdea.tracker, tradeIdea?.devSettings?.croupierUrl, ) } delete tradeIdea.pricing.credentials const _ideaKey = tradeIdea.content.ideaKey const _strategyKey = tradeIdea.strategy.reference || id() // only to debug // if ((tradeIdea.idea! as ITradeIdeaIdea).kind === 'adjust') { // ;(tradeIdea.idea as ITradeIdeaIdea).trade = undefined // ;(tradeIdea.idea as ITradeIdeaIdea).adjustment = { // kind: 'increase', // percentage: 100, // } // } // pack the idea pre const { nftMid, nftMidUrl, encryptedPublicKey, authSig, encryptedSymetricKeyRaw, symmetricKey, } = await packIdea(contract, tradeIdea, client, cached) let subscribers: string[] = [] if (tradeIdea.access !== undefined) { if (tradeIdea.access.wallets.length > 0) { subscribers = [...tradeIdea.access.wallets] } } const RULES_VERSION = 1 // create the idea const { nftId, strategyKey, ideaKey, ideaStageKey, transactionHash, cost, blockNumber, } = ixilyAssistedFiatMint !== undefined ? await ixilyAssistedFiatMint( contract, _strategyKey!, RULES_VERSION, nftMid, subscribers, _ideaKey, client, gasAggressivity, ) : await NftModule.mint.general.createIdea( contract, _strategyKey!, RULES_VERSION, nftMid, subscribers, _ideaKey, client, gasAggressivity, ) // tradeIdea.strategy.reference = strategyKey // tradeIdea.content.ideaNftId = Number(nftId) // tradeIdea.content.ideaStageKey = ideaStageKey // tradeIdea.content.ideaKey = ideaKey // if crypto is enabled if (encryptedPublicKey !== undefined) { log.dev( 'LitModule.updateToDefinitiveAccessControlConditions to be called:', ) await LitModule.updateToDefinitiveAccessControlConditions( encryptedSymetricKeyRaw!, symmetricKey!, nftId, authSig, tradeIdea?.isPublic, ) log.dev( '------------- LitModule.updateToDefinitiveAccessControlConditions called! -------------', ) } const result: IdeaCreationResult = { strategyKey, ideaKey, ideaStageKey, nftId, nftMid, nftMidUrl, transactionHash, blockNumber, cost, } return result } const getExistingNftByStrategyRefAndTickerWithCache = async ( strategyReference: string, ticker: string, filterKind?: CI.ITradeIdeaIdeaKind, creator?: string, contract?: IContract, ): Promise<{ tradeIdea: CI.ITradeIdea | undefined cached: { chainOfStrategy: IStrategyChain cachedStrategyState: CI.IStrategyState } }> => { contract = ContractModule.thisOrGet(contract) creator = await ContractModule.thisOrGetCreator(creator, contract) const chainOfStrategyWithCache = await IdeaTrafficDirectorModule.runSequentialTicket( [contract.gate!.address, creator, strategyReference], async () => { return getValidatedStrategyChainWithCache( strategyReference, creator, contract, false, ) }, ) const cached = { chainOfStrategy: chainOfStrategyWithCache.result, cachedStrategyState: chainOfStrategyWithCache.cache.cachedStrategy, } let idea: CI.ITradeIdea | undefined = undefined if (filterKind === undefined || filterKind === 'open') { idea = CONTRACT_TOOLS.queryChain.getExistingOpenNftByTicker( ticker, cached.cachedStrategyState, ) } else if (filterKind === 'adjust') { idea = CONTRACT_TOOLS.queryChain.getExistingAdjustNftByTicker( ticker, cached.cachedStrategyState, ) } else { idea = CONTRACT_TOOLS.queryChain.getExistingCloseNftByTicker( ticker, cached.cachedStrategyState, ) } if (idea !== undefined) { idea = copyObj(idea) await CryptoIdeasModule.restoreImages(idea) } return { tradeIdea: idea, cached, } } const getExistingNftByStrategyRefAndTicker = async ( strategyReference: string, ticker: string, filterKind?: CI.ITradeIdeaIdeaKind, creator?: string, contract?: IContract, ): Promise<CI.ITradeIdea | undefined> => { const result = await getExistingNftByStrategyRefAndTickerWithCache( strategyReference, ticker, filterKind, creator, contract, ) return result.tradeIdea } const createIdea = async ( payload: CI.ICreateIdeaRequest, gasAggressivity: number = 2, contract?: IContract, ixilyAssistedFiatMint?: IxilyAssistedFiatMint, cached?: { chainOfStrategy: IStrategyChain cachedStrategyState: CI.IStrategyState }, ): Promise<IdeaCreationResult> => { const idea = normalizeCreateIdeaRequest(payload) return createNormalizedTradeIdea( idea, gasAggressivity, contract, ixilyAssistedFiatMint, cached, ) } const getPortfolioStepFromSingleEntry = ( current: { [key: string]: number }, portfolio: IPortfolioAssetRequest, ): { kindStep: 'open' | 'adjust' | 'close' createIdeaRequest?: CI.ICreateIdeaRequest adjustIdeaRequest?: CI.IAdjustIdeaRequest closeIdeaRequest?: CI.ICloseIdeaRequest } => { const entry = portfolio.portfolioAsset let kindStep: 'open' | 'adjust' | 'close' let createIdeaRequest: CI.ICreateIdeaRequest | undefined = undefined let adjustIdeaRequest: CI.IAdjustIdeaRequest | undefined = undefined let closeIdeaRequest: CI.ICloseIdeaRequest | undefined = undefined assert( entry.notes !== undefined, 'Idea notes must not be undefined at first time creation', ) assert(typeof entry.notes === 'string', 'Idea notes must be string') assert(entry.notes.length > 0, 'Idea notes must be not void') if (current[entry.asset.ticker] !== undefined) { if (entry.allocation === 0) { kindStep = 'close' closeIdeaRequest = { ticker: entry.asset.ticker, strategyReference: portfolio.strategy.reference, reference: portfolio.portfolioAsset.reference, creatorWallet: portfolio.creator?.walletAddress, pricingCredentials: entry.pricing, notes: entry.notes, tracker: entry.tracker, } } else { kindStep = 'adjust' let kindIncDec: 'increase' | 'decrease' = 'increase' if (entry.allocation < current[entry.asset.ticker]) { kindIncDec = 'decrease' } const currentVal = current[entry.asset.ticker] const percentage = +BigNumber(currentVal) .minus(entry.allocation) .abs() .dividedBy(currentVal) .times(100) .toFixed(2) adjustIdeaRequest = { ticker: entry.asset.ticker, strategyReference: portfolio.strategy.reference, reference: portfolio.portfolioAsset.reference, creatorWallet: portfolio.creator?.walletAddress, pricingCredentials: entry.pricing, notes: entry.notes, tracker: entry.tracker, adjustment: { kind: kindIncDec, percentage, }, } } } else { kindStep = 'open' assert( entry.title !== undefined, 'Idea title must not be undefined at first time creation', ) assert(typeof entry.title === 'string', 'Idea title must be string') assert(entry.title!.length > 0, 'Idea title must be not void') assert( entry.asset.description !== undefined, 'Asset description must not be undefined at first time creation', ) assert( typeof entry.asset.description === 'string', 'Asset description must be string', ) assert( entry.asset.description!.length > 0, 'Asset description must be not void', ) assert( entry.direction !== undefined, 'Idea direction must not be undefined at first time creation', ) assert( typeof entry.direction === 'string', 'Idea direction must be string', ) assert( entry.direction === 'long' || entry.direction === 'short', 'Idea direction must be "long" or "short"', ) createIdeaRequest = { content: { reference: entry.reference!, }, strategy: { ...portfolio.strategy, ordersType: 'ALLOCATION', }, creator: { ...portfolio.creator, }, accessWallets: portfolio.accessWallets, idea: { title: entry.title!, asset: { ticker: entry.asset.ticker, description: entry.asset.description!, alternativeProviderSymbols: entry.asset.alternativeProviderSymbols, image: entry.asset.image, }, trade: { allocation: entry.allocation, direction: entry.direction!, }, notes: { commentary: entry.notes, }, }, pricing: entry.pricing, tracker: entry.tracker, } } const step: { kindStep: 'open' | 'adjust' | 'close' createIdeaRequest?: CI.ICreateIdeaRequest adjustIdeaRequest?: CI.IAdjustIdeaRequest closeIdeaRequest?: CI.ICloseIdeaRequest } = { kindStep, createIdeaRequest, adjustIdeaRequest, closeIdeaRequest, } return step } const createPortfolioEntry = async ( payload: IPortfolioAssetRequest, gasAggressivity: number = 2, contract?: IContract, ixilyAssistedFiatMint?: IxilyAssistedFiatMint, cached?: { chainOfStrategy: IStrategyChain cachedStrategyState: CI.IStrategyState }, ): Promise<IdeaCreationResult> => { contract = ContractModule.thisOrGet(contract) const creator = await ContractModule.thisOrGetCreator( payload.creator?.walletAddress, contract, ) const strategyWithCache = cached !== undefined ? { result: cached.chainOfStrategy, cache: { cachedStrategy: cached.cachedStrategyState, }, } : await getValidatedStrategyChainWithCache( payload.strategy.reference, creator, contract, false, ) cached = { chainOfStrategy: strategyWithCache.result, cachedStrategyState: strategyWithCache.cache.cachedStrategy, } // console.log('strategyWithCache') // console.log(strategyWithCache) const newStrategy = strategyWithCache.result.ideas.length === 0 if (newStrategy) { assert( payload.strategy.name !== undefined, 'Strategy name must not be undefined at first time creation', ) assert( typeof payload.strategy.name === 'string', 'Strategy name must be string', ) assert( payload.strategy.name!.length > 0, 'Strategy name must be not void', ) assert( payload.strategy.description !== undefined, 'Strategy description must not be undefined at first time creation', ) assert( typeof payload.strategy.description === 'string', 'Strategy description must be string', ) assert( payload.strategy.description!.length > 0, 'Strategy description must be not void', ) } let currentPortfolio = strategyWithCache.cache.cachedStrategy?.strategy?.portfolio if (currentPortfolio == undefined) { currentPortfolio = {} } const assetStepEntry = getPortfolioStepFromSingleEntry( currentPortfolio, payload, ) // console.log('assetStepEntry') // console.log(assetStepEntry) if (assetStepEntry.kindStep === 'open') { return createIdea( assetStepEntry.createIdeaRequest!, gasAggressivity, contract, ixilyAssistedFiatMint, cached, ) } else if (assetStepEntry.kindStep === 'adjust') { return (await adjustIdea( assetStepEntry.adjustIdeaRequest!, contract, false, gasAggressivity, ixilyAssistedFiatMint, )) as IdeaCreationResult } else { return (await closeIdea( assetStepEntry.closeIdeaRequest!, contract, false, gasAggressivity, ixilyAssistedFiatMint, )) as IdeaCreationResult } } const createPortfolio = async ( payload: IPortfolioRequest, gasAggressivity: number = 2, contract?: IContract, ixilyAssistedFiatMint?: IxilyAssistedFiatMint, cached?: { chainOfStrategy: IStrategyChain cachedStrategyState: CI.IStrategyState }, ): Promise< { step: { kindStep: 'open' | 'adjust' | 'close' | 'void' createIdeaRequest?: CI.ICreateIdeaRequest adjustIdeaRequest?: CI.IAdjustIdeaRequest closeIdeaRequest?: CI.ICloseIdeaRequest price?: CI.IValidPrice } result: IPortfolioResult }[] > => { // console.log('payload.portfolio') // console.log(payload.portfolio) // for (const p of payload.portfolio) { // console.log('portfolio Entry') // console.log(p) // } contract = ContractModule.thisOrGet(contract) const creator = await ContractModule.thisOrGetCreator( payload.creator?.walletAddress, contract, ) const strategyWithCache = cached !== undefined ? { result: cached.chainOfStrategy, cache: { cachedStrategy: cached.cachedStrategyState, }, } : await getValidatedStrategyChainWithCache( payload.strategy.reference, creator, contract, false, ) cached = { chainOfStrategy: strategyWithCache.result, cachedStrategyState: strategyWithCache.cache.cachedStrategy, } // console.log('strategyWithCache') // console.log(strategyWithCache) const newStrategy = strategyWithCache.result.ideas.length === 0 if (newStrategy) { assert( payload.strategy.name !== undefined, 'Strategy name must not be undefined at first time creation', ) assert( typeof payload.strategy.name === 'string', 'Strategy name must be string', ) assert( payload.strategy.name!.length > 0, 'Strategy name must be not void', ) assert( payload.strategy.description !== undefined, 'Strategy description must not be undefined at first time creation', ) assert( typeof payload.strategy.description === 'string', 'Strategy description must be string', ) assert( payload.strategy.description!.length > 0, 'Strategy description must be not void', ) } let currentPortfolio = strategyWithCache.cache.cachedStrategy?.strategy?.portfolio if (currentPortfolio === undefined) { currentPortfolio = {} } const portfolioReference = '' + Date.now() for (const okey of Object.keys(currentPortfolio!)) { if ( payload.portfolio.find((one) => one.asset.ticker === okey) === undefined ) { throw new Error( 'You must pass in adjustment or close for ticker "' + okey + '" in portfolio.', ) } } // console.log('payload.portfolio') // console.log(payload.portfolio) const portfolioSteps = payload.portfolio.map((portfolioAsset) => { portfolioAsset.reference = portfolioReference return getPortfolioStepFromSingleEntry(currentPortfolio!, { ...payload, portfolioAsset, }) }) // console.log('portfolioSteps') // console.log(portfolioSteps) // for (const stp of portfolioSteps) { // console.log('stp.adjustIdeaRequest') // console.log(stp.adjustIdeaRequest) // } // throw new Error('check above 3') const orderedPortfolioSteps: { kindStep: 'open' | 'adjust' | 'close' createIdeaRequest?: CI.ICreateIdeaRequest adjustIdeaRequest?: CI.IAdjustIdeaRequest closeIdeaRequest?: CI.ICloseIdeaRequest price?: CI.IValidPrice }[] = [] for (const step of portfolioSteps) { if (step.kindStep === 'close') { orderedPortfolioSteps.push(step) } } for (const step of portfolioSteps) { if (step.kindStep === 'adjust') { if (step.adjustIdeaRequest!.adjustment.kind === 'decrease') { orderedPortfolioSteps.push(step) } } } for (const step of portfolioSteps) { if (step.kindStep === 'adjust') { if (step.adjustIdeaRequest!.adjustment.kind === 'increase') { orderedPortfolioSteps.push(step) } } } for (const step of portfolioSteps) { if (step.kindStep === 'open') { orderedPortfolioSteps.push(step) } } const cachedStrategyState = copyObj( strategyWithCache.cache.cachedStrategy || {}, ) as CI.IStrategyState if (cachedStrategyState.allocationMap === undefined) { cachedStrategyState.allocationMap = new Map<string, number>() cachedStrategyState.strategy = payload.strategy cachedStrategyState.totalAllocated = 0 } for (const step of orderedPortfolioSteps) { if (step.kindStep === 'open') { const idea = step.createIdeaRequest!.idea if (currentPortfolio[idea.asset.ticker] !== undefined) { throw new Error( `The ticker ${idea.asset.ticker} is duplicated in the strategy.`, ) } if (idea.trade.allocation === undefined) { throw new Error( `The ticker new open ALLOCATION idea requires trade allocation.`, ) } if (idea.trade.allocation <= 0 || idea.trade.allocation > 100) { throw new Error( `The ticker new open ALLOCATION idea requires trade allocation between 1 and 100.`, ) } if (idea.trade.conviction !== undefined) { throw new Error( `The ticker new open ALLOCATION idea requires trade conviction to be undefined.`, ) } // if (!preMint) { // Adjust allocation after this idea // console.log('...........................................') // console.log('open') // console.log('idea.asset.ticker') // console.log(idea.asset.ticker) // console.log('idea.trade.allocation') // console.log(idea.trade.allocation) // console.log('cachedStrategyState.allocationMap') // console.log(cachedStrategyState.allocationMap) // console.log('cachedStrategyState.totalAllocated') // console.log(cachedStrategyState.totalAllocated) cachedStrategyState.allocationMap.set( idea.asset.ticker, 0 + idea.trade.allocation, ) currentPortfolio[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) { throw new Error( `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 // } } else if (step.kindStep === 'adjust') { const idea = step.adjustIdeaRequest! // console.log('idea.asset.ticker') // console.log(idea.asset.ticker) // console.log('tickers') // console.log(tickers) if (currentPortfolio[idea.ticker] === undefined) { throw new Error( `The ticker ${idea.ticker} is not defined in the strategy.`, ) } // Adjust allocation after this idea const previousAllocation = cachedStrategyState.allocationMap!.get( idea.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)) // newAllocation = parseFloat(newAllocation.toFixed(2)) // console.log('maxDecimals') // console.log(maxDecimals) // console.log('newAllocation') // console.log(newAllocation) // make sure newAllocation is not greater then 100 if (newAllocation > 100) { throw new Error( `Idea Not Allowed: With this idea, the allocation would be greater than 100.`, ) } cachedStrategyState.allocationMap!.set( idea.ticker, newAllocation, ) currentPortfolio[idea.ticker] = newAllocation // adjust total allocation cachedStrategyState.totalAllocated! = +BigNumber( cachedStrategyState.totalAllocated!, ).plus(BigNumber(newAllocation).minus(previousAllocation)) } 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 // console.log('previousAllocationDecimals') // console.log(previousAllocationDecimals) // console.log('idea.adjustment!.percentage.toString()') // console.log(idea.adjustment!.percentage.toString()) // const adjustmentDecimals = // idea.adjustment!.percentage.toString().split('.')[1] // ?.length || 0 // console.log('adjustmentDecimals') // console.log(adjustmentDecimals) // const maxDecimals = Math.max( // previousAllocationDecimals, // adjustmentDecimals, // ) // console.log('maxDecimals') // console.log(maxDecimals) // newAllocation = parseFloat(newAllocation.toFixed(maxDecimals)) // newAllocation = parseFloat(newAllocation.toFixed(2)) // make sure newAllocation is not zero if (newAllocation <= 0) { throw new Error( `Idea Not Allowed: With this idea, the allocation would be zero.`, ) } cachedStrategyState.allocationMap!.set( idea.ticker, newAllocation, ) // adjust total allocation cachedStrategyState.totalAllocated! = +BigNumber( cachedStrategyState.totalAllocated!, ) .plus(BigNumber(newAllocation).minus(previousAllocation)) .toFixed(2) } // console.log('...........................................') // console.log('adjust') // console.log('idea.asset.ticker') // console.log(idea.ticker) // console.log('idea.trade.allocation') // console.log(idea.adjustment) // console.log('cachedStrategyState.allocationMap') // console.log(cachedStrategyState.allocationMap) // console.log('cachedStrategyState.totalAllocated') // console.log(cachedStrategyState.totalAllocated) // 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) { throw new Error( `Idea Not Allowed: With this idea, the allocation would be greater than 100.`, ) } } else { const idea = step.closeIdeaRequest! if (currentPortfolio[idea.ticker] === undefined) { throw new Error( `The ticker ${idea.ticker} is not defined in the strategy.`, ) } // Adjust allocation after this idea const previousAllocation = cachedStrategyState.allocationMap!.get( idea.ticker, )! cachedStrategyState.totalAllocated! = +BigNumber( cachedStrategyState.totalAllocated!, ) .minus(previousAllocation) .toFixed(2) cachedStrategyState.allocationMap!.delete(idea.ticker)! delete currentPortfolio[idea.ticker] // check total allocation if (cachedStrategyState.totalAllocated! > 100) { throw new Error( `Idea Not Allowed: With this idea, the allocation would be greater than 100.`, ) } // console.log('...........................................') // console.log('close') // console.log('idea.asset.ticker') // console.log(idea.ticker) // console.log('cachedStrategyState.allocationMap') // console.log(cachedStrategyState.allocationMap) // console.log('cachedStrategyState.totalAllocated') // console.log(cachedStrategyState.totalAllocated) // 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 } } // make sure all prices would work before try open all const pricedOrderedPortfolioSteps = await Promise.all( orderedPortfolioSteps.map((eachStep) => { return (async () => { if (eachStep.kindStep === 'open') { eachStep.price = await getPricingInfo( eachStep.createIdeaRequest!.pricing, eachStep.createIdeaRequest!.idea!.asset.ticker, eachStep.createIdeaRequest!.tracker, eachStep.createIdeaRequest!.devSettings?.croupierUrl, ) } else if (eachStep.kindStep === 'adjust') { eachStep.price = await getPricingInfo( eachStep.adjustIdeaRequest!.pricingCredentials, eachStep.adjustIdeaRequest!.ticker, eachStep.adjustIdeaRequest!.tracker, ) } else if (eachStep.kindStep === 'close') { eachStep.price = await getPricingInfo( eachStep.closeIdeaRequest!.pricingCredentials, eachStep.closeIdeaRequest!.ticker, eachStep.closeIdeaRequest!.tracker, ) } return eachStep })() }), ) // console.log('pricedOrderedPortfolioSteps') // console.log(pricedOrderedPortfolioSteps) // for (const one of pricedOrderedPortfolioSteps) { // console.log('one.adjustIdeaRequest') // console.log(one.adjustIdeaRequest) // } const pricedOrderedOpen: { kindStep: 'open' | 'adjust' | 'close' | 'void' createIdeaRequest?: CI.ICreateIdeaRequest adjustIdeaRequest?: CI.IAdjustIdeaRequest closeIdeaRequest?: CI.ICloseIdeaRequest price?: CI.IValidPrice }[] = [] const pricedOrderedAdjust: { kindStep: 'open' | 'adjust' | 'close' | 'void' createIdeaRequest?: CI.ICreateIdeaRequest adjustIdeaRequest?: CI.IAdjustIdeaRequest closeIdeaRequest?: CI.ICloseIdeaRequest price?: CI.IValidPrice }[] = [] const pricedOrderedAdjustIncrease: { kindStep: 'open' | 'adjust' | 'close' | 'void' createIdeaRequest?: CI.ICreateIdeaRequest adjustIdeaRequest?: CI.IAdjustIdeaRequest closeIdeaRequest?: CI.ICloseIdeaRequest price?: CI.IValidPrice }[] = [] const pricedOrderedClose: { kindStep: 'open' | 'adjust' | 'close' | 'void' createIdeaRequest?: CI.ICreateIdeaRequest adjustIdeaRequest?: CI.IAdjustIdeaRequest closeIdeaRequest?: CI.ICloseIdeaRequest price?: CI.IValidPrice }[] = [] for (const step of pricedOrderedPortfolioSteps) { if (step.kindStep === 'open') { pricedOrderedOpen.push(step) } else if (step.kindStep === 'adjust') { if (step.adjustIdeaRequest!.adjustment.kind === 'increase') { pricedOrderedAdjustIncrease.push(step) } else { pricedOrderedAdjust.push(step) } } else { pricedOrderedClose.push(step) } } // console.log('currentPortfolio') // console.log(currentPortfolio) // console.log('pricedOrderedClose') // console.log(pricedOrderedClose) // console.log('pricedOrderedAdjust') // console.log(pricedOrderedAdjust) // console.log('pricedOrderedAdjustIncrease') // console.log(pricedOrderedAdjustIncrease) // for (const one of pricedOrderedAdjustIncrease) { // console.log('one.adjustIdeaRequest!') // console.log(one.adjustIdeaRequest!) // } // console.log('pricedOrderedOpen') // console.log(pricedOrderedOpen) // pricedOrderedAdjust.map((each) => console.log(each)) // pricedOrderedOpen.map((each) => console.log(each)) type ErrorDealer = (err: any) => IdeaCreationFailed const toError = (strategyKey: string, ticker: string): ErrorDealer => { return (error: any): IdeaCreationFailed => { const errObj: IdeaCreationFailed = { strategyKey, ticker, error, } return errObj } } // first the closes, then adjusts, then opens to avoid allocation validation error // console.log('DOING CLOSES') // console.log('DOING CLOSES') // console.log('DOING CLOSES') const resultsClose = await Promise.all( pricedOrderedClose.map((clStep) => { return (async () => { // we can reuse the price here clStep.closeIdeaRequest!.priceInfo = clStep.price return { step: clStep, result: await closeIdea( clStep.closeIdeaRequest!, contract, false, gasAggressivity, ixilyAssistedFiatMint, ).catch( toError( clStep.closeIdeaRequest!.strategyReference, clStep.closeIdeaRequest!.ticker, ), ), } })() }), ) // console.log('resultsClose') // console.log(resultsClose) // console.log('DOING ADJUSTS') // console.log('DOING ADJUSTS') // console.log('DOING ADJUSTS') const resultsAdjust = await Promise.all( pricedOrderedAdjust.map((adStep) => { return (async () => { // a this point the price is too old to reuse and will fail validation // adStep.adjustIdeaRequest!.priceInfo = adStep.price return { step: adStep, result: await adjustIdea( adStep.adjustIdeaRequest!, contract, false, gasAggressivity, ixilyAssistedFiatMint, ).catch( toError( adStep.adjustIdeaRequest!.strategyReference, adStep.adjustIdeaRequest!.ticker, ), ), } })() }), ) const resultsAdjustIncrease = await Promise.all( pricedOrderedAdjustIncrease.map((adStep) => { return (async () => { // a this point the price is too old to reuse and will fail validation // adStep.adjustIdeaRequest!.priceInfo = adStep.price return { step: adStep, result: await adjustIdea( adStep.adjustIdeaRequest!, contract, false, gasAggressivity, ixilyAssistedFiatMint, ).catch( toError( adStep.adjustIdeaRequest!.strategyReference, adStep.adjustIdeaRequest!.ticker, ), ), } })() }), ) // console.log('resultsAdjust') // console.log(resultsAdjust) // console.log('DOING OPENS') // console.log('DOING OPENS') // console.log('DOING OPENS') const resultsOpen = await Promise.all( pricedOrderedOpen.map((opStep) => { // a this point the price is too old to reuse and will fail validation // opStep.createIdeaRequest!.priceInfo = opStep.price return (async () => { return { step: opStep, result: await createIdea( opStep.createIdeaRequest!, gasAggressivity, contract, ixilyAssistedFiatMint, ).catch( toError( opStep.createIdeaRequest!.strategy.reference, opStep.createIdeaRequest!.idea.asset.ticker, ), ), } })() }), ) // console.log('resultsOpen') // console.log(resultsOpen) const results = [ ...resultsClose, ...resultsAdjust, ...resultsAdjustIncrease, ...resultsOpen, ] // console.log('results') // console.log(results) return results as { step: { kindStep: 'open' | 'adjust' | 'close' | 'void' createIdeaRequest?: CI.ICreateIdeaRequest adjustIdeaRequest?: CI.IAdjustIdeaRequest closeIdeaRequest?: CI.ICloseIdeaRequest price?: CI.IValidPrice } result: IPortfolioResult }[] } const normalizeCreateIdeaRequest = ( createIdeaRequest: CI.ICreateIdeaRequest, ): CI.ITradeIdea => { const idea: CI.ITradeIdeaIdea = { title: createIdeaRequest.idea.title, kind: 'open', asset: { ticker: createIdeaRequest.idea.asset.ticker, description: createIdeaRequest.idea.asset.description, alternativeProviderSymbols: createIdeaRequest.idea.asset.alternativeProviderSymbols, image: createIdeaRequest.idea.asset.image, }, trade: { conviction: createIdeaRequest.idea.trade.conviction, allocation: createIdeaRequest.idea.trade.allocation, direction: createIdeaRequest.idea.trade.direction, }, notes: { commentary: createIdeaRequest.idea.notes.commentary, }, priceInfo: createIdeaRequest.priceInfo, } const strategy: CI.ITradeIdeaStrategy = { reference: createIdeaRequest.strategy.reference, name: createIdeaRequest.strategy.name, description: createIdeaRequest.strategy.description, image: createIdeaRequest.strategy.image, externalLink: createIdeaRequest.strategy.externalLink, ordersType: createIdeaRequest.strategy.ordersType, } const pricing = createIdeaRequest.pricing const access: CI.ITradeIdeaAccess | undefined = createIdeaRequest.accessWallets === undefined ? undefined : ({ wallets: createIdeaRequest.accessWallets, } as CI.ITradeIdeaAccess) const content: CI.ITradeIdeaContent = { reference: createIdeaRequest.content.reference, } const creator: CI.ITradeIdeaCreator = { name: createIdeaRequest.creator.name, company: createIdeaRequest.creator.company, url: createIdeaRequest.creator.url, companyLogo: createIdeaRequest.creator.companyLogo, } const devSettings = createIdeaRequest.devSettings const tradeIdea: CI.ITradeIdea = { content, strategy, creator, access, idea, pricing, devSettings, } return tradeIdea } const createNormalizedTradeIdea = async ( tradeIdea: CI.ITradeIdea, gasAggressivity: number = 2, contract?: IContract, ixilyAssistedFiatMint?: IxilyAssistedFiatMint, cached?: { chainOfStrategy: IStrategyChain cachedStrategyState: CI.IStrategyState }, ): Promise<IdeaCreationResult> => { //return retryFunctionHelper<IdeaCreationResult>({ // maxRetries: 3, // retryCallback: async () => { contract = ContractModule.thisOrGet(contract) const creator = await ContractModule.thisOrGetCreator(undefined, contract) // creates a pool per ticker to avoid errors return IdeaTrafficDirectorModule.runSequentialTicket( [ 'createIdea', contract.gate!.address, creator, tradeIdea.strategy.reference, (tradeIdea.idea as CI.ITradeIdeaIdea).asset.ticker, ], async () => { return coreCreateIdea( tradeIdea, contract!, undefined, gasAggressivity, ixilyAssistedFiatMint, cached, ) }, ) // }, // rejectOnMaxRetries: true, // }) as Promise<IdeaCreationResult> } const adjustIdea = async ( payload: CI.IAdjustIdeaRequest, contract?: IContract, adjustDisabled: boolean