@ixily/activ
Version:
Alpha Capture Trade Idea Verification. Blockchain ownership proven trade ideas and strategies.
1,069 lines • 81.9 kB
JavaScript
"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