@ixily/activ
Version:
Alpha Capture Trade Idea Verification. Blockchain ownership proven trade ideas and strategies.
1,819 lines (1,676 loc) • 85 kB
text/typescript
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