UNPKG

@ixily/activ

Version:

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

1,069 lines 81.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProvableIdeasModule = void 0; const axios_1 = __importDefault(require("axios")); const bignumber_js_1 = require("bignumber.js"); require("isomorphic-fetch"); const __1 = require("../../"); const assert = (toCheck, errorMsg) => { if (!toCheck) { throw new Error(errorMsg); } }; // type DataFilter = 'all' | 'my' | 'accesible' | 'public' const state = { contractNetworkParallelPools: [], worker: undefined, workerLock: false, }; const processImage = async (img, format = 'profile') => { if (img !== undefined) { const goodOne = !(0, __1.isNullOrWhiteSpace)(img.b64) ? img.b64 : img.url; if (!(0, __1.isNullOrWhiteSpace)(goodOne)) { if ((0, __1.isNullOrWhiteSpace)(img.cid)) { const stringBase64 = await __1.ImagesModule.convertOrMinifyImageToBase64(goodOne, format); const resultStored = await __1.NftStoreModule.storeJsonContent(stringBase64); img.cid = resultStored.cid; } } delete img.b64; delete img.url; } }; const processImages = async (tradeIdea) => { const ps = []; ps.push(async () => { await processImage(tradeIdea.idea.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, strategyChain, contract) => { // check first if the current idea is adjust or close const idea = tradeIdea.idea; 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 = __1.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 __1.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, tradeIdea, creator, cached) => { // console.log('tradeIdea') // console.log(tradeIdea) let encryptedPublicKey; let encryptedSymetricKeyRaw; let symmetricKey; let encryptedIdea; let authSig; contract = __1.ContractModule.thisOrGet(contract); creator = await __1.ContractModule.thisOrGetCreator(creator, contract); // add creator wallet tradeIdea.creator.walletAddress = creator; // tradeIdea.strategy.creatorWallet = creator // tradeIdea.creator.name = tradeIdea.creator.name tradeIdea.strategy.uniqueKey = (0, __1.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.kind === 'open') { delete tradeIdea.content.ideaKey; } let chainOfStrategy; let cachedStrategyState; if (cached !== undefined) { chainOfStrategy = cached.chainOfStrategy; cachedStrategyState = cached.cachedStrategyState; } else { const chainOfStrategyWithCache = await __1.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.priceInfo.price.timestamp; } const chronological = __1.StrategyChainModule.chronologicalIdeasFromChain(chainOfStrategy); tradeIdea.creator.walletAddress = creator; const tradeIdeaIdea = tradeIdea.idea; // 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 = __1.CONTRACT_TOOLS.deterministicStringify(tradeIdea.idea); if (deterministicIdeaString === undefined) { throw new Error('PROVABLE IDEAS MODULE ERROR: deterministicIdeaString is undefined'); } tradeIdea.ideaSha512Hash = __1.CryptoJs.SHA512(deterministicIdeaString).toString(); __1.LogModule.dev('tradeIdea.ideaSha512Hash:'); __1.LogModule.dev(tradeIdea.ideaSha512Hash); // console.log('chronological') // console.log(chronological) // console.log('tradeIdea') // console.log(tradeIdea) tradeIdeaIdea.chainIdeas = []; const validation = await __1.RULES.v1.rules([tradeIdea], (0, __1.copyObj)(cachedStrategyState), true); if (typeof validation === 'string') { throw new Error(('Invalid idea: ' + validation)); } // tradeIdea.strategy = validation.cachedStrategyState!.strategy if (tradeIdea.access !== undefined) { const stored = await __1.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); __1.LogModule.dev('pastIdeas:'); __1.LogModule.dev(pastIdeas); // throw new Error('check above') const chainIdeas = []; for (const pastIdea of pastIdeas) { let nftId = null; if (pastIdea?.nftId?.type === 'BigNumber') { nftId = Number(pastIdea?.nftId?.hex); } else { nftId = Number(pastIdea?.nftId); } const pastIdeaIdea = pastIdea.idea; const pastIdeaHash = pastIdea.ideaSha512Hash; const chainIdea = { nftId, reference: pastIdea.content.reference, clientTokenId: pastIdea.content.clientTokenId, ideaStageKey: pastIdea.content.ideaStageKey, ideaKey: pastIdea.content.ideaKey, url: pastIdea.url, idea: pastIdeaIdea, ideaSha512Hash: pastIdeaHash, }; chainIdeas.push(chainIdea); } tradeIdeaIdea.chainIdeas = chainIdeas; // Second: We must calculate the idea performance const ideaPerformance = __1.IdeaPerformanceModule.getIdeaPerformance([ ...pastIdeas, tradeIdea, ]); tradeIdea.idea.result = ideaPerformance; await processImages(tradeIdea); const deterministicIdeaString = __1.CONTRACT_TOOLS.deterministicStringify(tradeIdea.idea); if (deterministicIdeaString === undefined) { throw new Error('PROVABLE IDEAS MODULE ERROR: deterministicIdeaString is undefined'); } tradeIdea.ideaSha512Hash = __1.CryptoJs.SHA512(deterministicIdeaString).toString(); // Third: We must check if the idea is valid const validation = await __1.RULES.v1.rules([tradeIdea], (0, __1.copyObj)(cachedStrategyState), true); if (typeof validation === 'string') { throw new Error(('Invalid idea: ' + validation)); } // 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 __1.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 = []; for (const pastIdea of pastIdeas) { let nftId = null; if (pastIdea?.nftId?.type === 'BigNumber') { nftId = Number(pastIdea?.nftId?.hex); } else { nftId = Number(pastIdea?.nftId); } const pastIdeaIdea = pastIdea.idea; const pastIdeaHash = pastIdea.ideaSha512Hash; const chainIdea = { nftId, reference: pastIdea.content.reference, clientTokenId: pastIdea.content.clientTokenId, ideaStageKey: pastIdea.content.ideaStageKey, ideaKey: pastIdea.content.ideaKey, url: pastIdea.url, idea: pastIdeaIdea, ideaSha512Hash: pastIdeaHash, }; chainIdeas.push(chainIdea); } tradeIdeaIdea.chainIdeas = chainIdeas; await processImages(tradeIdea); const deterministicIdeaString = __1.CONTRACT_TOOLS.deterministicStringify(tradeIdea.idea); if (deterministicIdeaString === undefined) { throw new Error('PROVABLE IDEAS MODULE ERROR: deterministicIdeaString is undefined'); } tradeIdea.ideaSha512Hash = __1.CryptoJs.SHA512(deterministicIdeaString).toString(); // Second: We must calculate the idea performance const ideaPerformance = __1.IdeaPerformanceModule.getIdeaPerformance([ ...pastIdeas, tradeIdea, ]); tradeIdea.idea.result = ideaPerformance; // Third: We must check if the idea is valid again const validation = await __1.RULES.v1.rules([tradeIdea], (0, __1.copyObj)(cachedStrategyState), true); if (typeof validation === 'string') { throw new Error(('Invalid idea: ' + validation)); } // tradeIdea.strategy = validation.cachedStrategyState.strategy } const stringified = __1.CONTRACT_TOOLS.deterministicStringify(tradeIdea); if (stringified === undefined) { throw new Error('PROVABLE IDEAS MODULE ERROR: stringify open sea metadata failed'); } const resultStored = await __1.NftStoreModule.storeJsonContent(stringified); const nftMid = resultStored.cid; const nftMidUrl = resultStored.url; return { nftMidUrl, nftMid, encryptedIdea, encryptedPublicKey, encryptedSymetricKeyRaw, symmetricKey, authSig, }; }; const getPricingInfo = async (pricing, ticker, tracker, croupierUrl) => { // 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; // TEMP HOTLOG // console.log('ACTIV: getPricingInfo: tradeIdea.tracker: ', tradeIdea.tracker) pricing = (0, __1.copyObj)(pricing); priceDetailsFromLitAction = await __1.ProvableModule.getVerifiedPriceDetails(ticker, pricing.provider, pricing.credentials, { tracker, croupierUrl, }); __1.LogModule.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, contract, client, gasAggressivity = 2, ixilyAssistedFiatMint, cached) => { __1.LogModule.dev('++++++++++++++++++++++++++++++++++++++++++++++++++++++++'); const tracker = tradeIdea?.tracker || null; __1.LogModule.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; __1.LogModule.dev('providerCreateIdea (symbol)', ideaPayload.asset.ticker); __1.LogModule.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 || (0, __1.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 = []; 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 __1.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) { __1.LogModule.dev('LitModule.updateToDefinitiveAccessControlConditions to be called:'); await __1.LitModule.updateToDefinitiveAccessControlConditions(encryptedSymetricKeyRaw, symmetricKey, nftId, authSig, tradeIdea?.isPublic); __1.LogModule.dev('------------- LitModule.updateToDefinitiveAccessControlConditions called! -------------'); } const result = { strategyKey, ideaKey, ideaStageKey, nftId, nftMid, nftMidUrl, transactionHash, blockNumber, cost, }; return result; }; const getExistingNftByStrategyRefAndTickerWithCache = async (strategyReference, ticker, filterKind, creator, contract) => { contract = __1.ContractModule.thisOrGet(contract); creator = await __1.ContractModule.thisOrGetCreator(creator, contract); const chainOfStrategyWithCache = await __1.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 = undefined; if (filterKind === undefined || filterKind === 'open') { idea = __1.CONTRACT_TOOLS.queryChain.getExistingOpenNftByTicker(ticker, cached.cachedStrategyState); } else if (filterKind === 'adjust') { idea = __1.CONTRACT_TOOLS.queryChain.getExistingAdjustNftByTicker(ticker, cached.cachedStrategyState); } else { idea = __1.CONTRACT_TOOLS.queryChain.getExistingCloseNftByTicker(ticker, cached.cachedStrategyState); } if (idea !== undefined) { idea = (0, __1.copyObj)(idea); await __1.CryptoIdeasModule.restoreImages(idea); } return { tradeIdea: idea, cached, }; }; const getExistingNftByStrategyRefAndTicker = async (strategyReference, ticker, filterKind, creator, contract) => { const result = await getExistingNftByStrategyRefAndTickerWithCache(strategyReference, ticker, filterKind, creator, contract); return result.tradeIdea; }; const createIdea = async (payload, gasAggressivity = 2, contract, ixilyAssistedFiatMint, cached) => { const idea = normalizeCreateIdeaRequest(payload); return createNormalizedTradeIdea(idea, gasAggressivity, contract, ixilyAssistedFiatMint, cached); }; const getPortfolioStepFromSingleEntry = (current, portfolio) => { const entry = portfolio.portfolioAsset; let kindStep; let createIdeaRequest = undefined; let adjustIdeaRequest = undefined; let closeIdeaRequest = 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'; if (entry.allocation < current[entry.asset.ticker]) { kindIncDec = 'decrease'; } const currentVal = current[entry.asset.ticker]; const percentage = +(0, bignumber_js_1.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, createIdeaRequest, adjustIdeaRequest, closeIdeaRequest, }; return step; }; const createPortfolioEntry = async (payload, gasAggressivity = 2, contract, ixilyAssistedFiatMint, cached) => { contract = __1.ContractModule.thisOrGet(contract); const creator = await __1.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)); } else { return (await closeIdea(assetStepEntry.closeIdeaRequest, contract, false, gasAggressivity, ixilyAssistedFiatMint)); } }; const createPortfolio = async (payload, gasAggressivity = 2, contract, ixilyAssistedFiatMint, cached) => { // console.log('payload.portfolio') // console.log(payload.portfolio) // for (const p of payload.portfolio) { // console.log('portfolio Entry') // console.log(p) // } contract = __1.ContractModule.thisOrGet(contract); const creator = await __1.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 = []; 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 = (0, __1.copyObj)(strategyWithCache.cache.cachedStrategy || {}); if (cachedStrategyState.allocationMap === undefined) { cachedStrategyState.allocationMap = new Map(); 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 = +(0, bignumber_js_1.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 = {}; 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 = +(0, bignumber_js_1.BigNumber)(previousAllocation) .plus((0, bignumber_js_1.BigNumber)(previousAllocation).times((0, bignumber_js_1.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 = +(0, bignumber_js_1.BigNumber)(cachedStrategyState.totalAllocated).plus((0, bignumber_js_1.BigNumber)(newAllocation).minus(previousAllocation)); } else { const newAllocation = +(0, bignumber_js_1.BigNumber)(previousAllocation) .minus((0, bignumber_js_1.BigNumber)(previousAllocation).times((0, bignumber_js_1.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 = +(0, bignumber_js_1.BigNumber)(cachedStrategyState.totalAllocated) .plus((0, bignumber_js_1.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 = {}; 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 = +(0, bignumber_js_1.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 = {}; 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 = []; const pricedOrderedAdjust = []; const pricedOrderedAdjustIncrease = []; const pricedOrderedClose = []; 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); } } const toError = (strategyKey, ticker) => { return (error) => { const errObj = { 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; }; const normalizeCreateIdeaRequest = (createIdeaRequest) => { const idea = { 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 = { 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 = createIdeaRequest.accessWallets === undefined ? undefined : { wallets: createIdeaRequest.accessWallets, }; const content = { reference: createIdeaRequest.content.reference, }; const creator = { name: createIdeaRequest.creator.name, company: createIdeaRequest.creator.company, url: createIdeaRequest.creator.url, companyLogo: createIdeaRequest.creator.companyLogo, }; const devSettings = createIdeaRequest.devSettings; const tradeIdea = { content, strategy, creator, access, idea, pricing, devSettings, }; return tradeIdea; }; const createNormalizedTradeIdea = async (tradeIdea, gasAggressivity = 2, contract, ixilyAssistedFiatMint, cached) => { //return retryFunctionHelper<IdeaCreationResult>({ // maxRetries: 3, // retryCallback: async () => { contract = __1.ContractModule.thisOrGet(contract); const creator = await __1.ContractModule.thisOrGetCreator(undefined, contract); // creates a pool per ticker to avoid errors return __1.IdeaTrafficDirectorModule.runSequentialTicket([ 'createIdea', contract.gate.address, creator, tradeIdea.strategy.reference, tradeIdea.idea.asset.ticker, ], async () => { return coreCreateIdea(tradeIdea, contract, undefined, gasAggressivity, ixilyAssistedFiatMint, cached); }); // }, // rejectOnMaxRetries: true, // }) as Promise<IdeaCreationResult> }; const adjustIdea = async (payload, contract, adjustDisabled = false, gasAggressivity = 2, ixilyAssistedFiatMint) => { // return retryFunctionHelper<IdeaCreationResult | ITradeIdea>({ // maxRetries: 3, // retryCallback: async () => { contract = __1.ContractModule.thisOrGet(contract); const filterKind = 'open'; let result = undefined; let attempts = 0; while ((0, __1.isNullOrUndefined)(result?.tradeIdea) && attempts < 10) { result = await getExistingNftByStrategyRefAndTickerWithCache(payload.strategyReference, payload.ticker, filterKind, payload?.creatorWallet, contract); attempts++; if ((0, __1.isNullOrUndefined)(result?.tradeIdea)) { await (0, __1.rest)(500); } } if ((0, __1.isNullOrUndefined)(result?.tradeIdea)) { throw new Error('No investment idea for this asset within strategy reference "' + payload.strategyReference + '" could be found'); } const cached = result.cached; const tradeIdea = result.tradeIdea; const id