UNPKG

@ixily/activ

Version:

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

659 lines (601 loc) 15.8 kB
import { BigNumber } from 'ethers' import { BehaviorSubject } from 'rxjs' import { ethers, IContract, IdeaTrafficDirectorModule, IOperationalCost, LogModule as log, OperationalCostModule, } from '../..' const createIdeaCore = async ( contract: IContract, _strategyKey: string, _rulesVersion: number, _metadata: string, _subscribers: string[], _ideaKey?: number, clientInBehalf?: string, gasAggressivity: number = 2, ): Promise<{ nftId: number strategyKey: string ideaKey: number ideaStageKey: number openseaUrl: string transactionHash: string blockNumber: number cost: IOperationalCost }> => { log.dev('pre commit') const callerAddress = clientInBehalf !== undefined ? clientInBehalf : await contract.gate!.signer.getAddress() log.dev(`Caller address: ${callerAddress}`) log.dev(`Strategy key: ${_strategyKey}`) log.dev(`Idea key: ${_ideaKey}`) const balance = await contract.gate!.signer.getBalance() const balanceInEth = ethers.utils.formatEther(balance) log.dev(`Caller balance: ${Number(balanceInEth).toFixed(4)} ETH`) // to avoid twice data // if the user is a owner and this appears in the subscribers list, "remove" it // (i.e. apply the filer) _subscribers = _subscribers?.filter( (address) => address?.toLowerCase() !== callerAddress?.toLowerCase(), ) || [] log.dev(`Subscribers post:`, _subscribers) log.dev(`clientInBehalf:`, clientInBehalf) log.dev('_ideaKey:', _ideaKey) const costPrior = await OperationalCostModule.getEstimatedCosts( contract, clientInBehalf, _ideaKey === undefined, ) const APPLY_SETTINGS = true let optionalSettings: any = {} if (APPLY_SETTINGS) { // const gasLimit = costPrior.estimatedComputationalRawCost const gasLimit = costPrior.estimatedComputationalRawCost + Math.floor((1 / 10) * costPrior.estimatedComputationalRawCost) const gasPrice = costPrior.estimatedGasPrice.weiBigNumber.add( costPrior.estimatedGasPrice.weiBigNumber .mul(gasAggressivity) .div(100), ) const value = costPrior.ixilyTax.weiBigNumber log.dev('currentIxilyTax:') log.dev(value) optionalSettings = { gasLimit, gasPrice, value, } } if (optionalSettings.value === 0 || optionalSettings.value === undefined) { throw new Error('optionalSettings.value is 0 or undefined') } // console.log('costPrior:') // console.log(costPrior) const dealErrIfAboutCost = async (err: Error) => { if (err.message) { for (const increaseIndex of contract.recipe.errorsDealWith .increaseGasUnits) { if (err.message.includes(increaseIndex)) { await OperationalCostModule.increaseEstimatedComputationalRawCost( contract.gate!.address, _ideaKey === undefined, clientInBehalf !== undefined, ) } } for (const decreaseIndex of contract.recipe.errorsDealWith .decreaseGasUnits) { if (err.message.includes(decreaseIndex)) { await OperationalCostModule.decreaseEstimatedComputationalRawCost( contract.gate!.address, _ideaKey === undefined, clientInBehalf !== undefined, ) } } } // console.log('err') // console.log(err) // console.log('err.message') // console.log(err.message) throw err } const nftCommit = clientInBehalf === undefined ? _ideaKey === undefined ? await contract .gate!.createIdea( _strategyKey, _rulesVersion, _metadata, _subscribers, optionalSettings, ) .catch(dealErrIfAboutCost) : await contract .gate!.createIdeaStage( _strategyKey, _rulesVersion, _metadata, _subscribers, _ideaKey, optionalSettings, ) .catch(dealErrIfAboutCost) : _ideaKey === undefined ? await contract .gate!.providerCreateIdea( clientInBehalf, _strategyKey, _rulesVersion, _metadata, _subscribers, optionalSettings, ) .catch(dealErrIfAboutCost) : await contract .gate!.providerCreateIdeaStage( clientInBehalf, _strategyKey, _rulesVersion, _metadata, _subscribers, _ideaKey, optionalSettings, ) .catch(dealErrIfAboutCost) log.dev('nftCommit') log.dev(nftCommit) const nftCommitted = await nftCommit.wait().catch(dealErrIfAboutCost) log.dev('createNftIdea (nftCommitted)') log.dev(nftCommitted) const abiForEvent = [ 'event IdeaCreated(address,uint256,string,uint256,uint256,uint256)', ] const ifaceForEvent = new ethers.utils.Interface(abiForEvent) const parsedLogs: any[] = [] for (const logIn of nftCommitted.logs) { try { const parsedLog = ifaceForEvent.parseLog(logIn) parsedLogs.push(parsedLog) } catch (e) {} } // console.log('nftCommitted logs') // console.log(parsedLogs) if (parsedLogs.length !== 1) { throw new Error('IdeaNftCreation: IdeaCreated event not found') } // console.log('nftCommitted events') // console.log(nftCommitted.events!) // for (const evt of nftCommitted.events!) { // console.log('event:') // console.log(evt) // console.log('topics:') // console.log(evt.topics) // } // const targetEvent = nftCommitted.events!.find( // (one: any) => one.event === 'IdeaCreated', // ) const targetEvent = parsedLogs[0]! if (targetEvent == undefined) { throw new Error('IdeaNftCreation: IdeaCreated event not found') } else { // log.dev(targetEvent.args) const nftId = targetEvent.args[1] const strategyKey = targetEvent.args[2] const ideaKey = targetEvent.args[3] const ideaStageKey = targetEvent.args[4] if (_strategyKey === strategyKey) { const openseaUrl = `https://testnets.opensea.io/assets/${ nftCommitted.to }/${nftId.toNumber()}` const costAfter = await OperationalCostModule.getFinalCosts( costPrior, nftCommitted, contract, clientInBehalf, _ideaKey === undefined, ) // console.log('costAfter') // console.log(costAfter) return { nftId: (nftId as BigNumber).toNumber(), strategyKey: _strategyKey, ideaKey: ideaKey.toNumber(), ideaStageKey: ideaStageKey.toNumber(), openseaUrl, transactionHash: nftCommitted.transactionHash, blockNumber: nftCommitted.blockNumber, cost: costAfter, } } else { throw new Error('IdeaNftCreation: Strategy key mismatch') } } } const createIdea = async ( contract: IContract, _strategyKey: string, _rulesVersion: number, _metadata: string, _subscribers: string[], _ideaKey?: number, clientInBehalf?: string, gasAggressivity: number = 2, ): Promise<{ nftId: number strategyKey: string ideaKey: number ideaStageKey: number openseaUrl: string transactionHash: string blockNumber: number cost: IOperationalCost }> => { return IdeaTrafficDirectorModule.runSequentialTicket( [clientInBehalf || ''], async (): Promise<{ nftId: number strategyKey: string ideaKey: number ideaStageKey: number openseaUrl: string transactionHash: string blockNumber: number cost: IOperationalCost }> => { const result = await createIdeaCore( contract, _strategyKey, _rulesVersion, _metadata, _subscribers, _ideaKey, clientInBehalf, gasAggressivity, ) return result }, ) } const giveIdeaTo = async ( contract: IContract, _ideaNftId: number, _clientAddresses: string[], ) => { log.dev('_clientAddresses:') log.dev(_clientAddresses) const nftCommitted = await contract.gate!.giveIdeaTo( _ideaNftId, _clientAddresses, { gasLimit: 2000000, }, ) await nftCommitted.wait() } /* * Returns the list of walllet addresses that have access to the idea * * @param contract The contract instance * @param _ideaNftId The idea NFT id * @returns The list of wallet addresses */ const getIdeaViewers = async ( contract: IContract, _ideaNftId: number, ): Promise<string[]> => { const ideaViewers = await contract.gate!.getIdeaViewers(_ideaNftId) return ideaViewers as string[] } const watchEvents = async ( contract: IContract, ): Promise< BehaviorSubject<{ network: string contractAddress: string }> > => { const ideaFilter = contract.gate!.filters.IdeaCreated() const ideaEvent$ = new BehaviorSubject<{ network: string contractAddress: string event?: { creatorAddress: string nftId: number strategyKey: string ideaKey: number stageKey: number blockNumber: number } }>({ network: contract.recipe.chain, contractAddress: contract.gate!.address, }) // console.log('Watching the Mint events...') contract.gate!.on(ideaFilter, (...args) => { ideaEvent$.next({ network: contract.recipe.chain, contractAddress: contract.gate!.address, event: { creatorAddress: args[0], nftId: args[1].toNumber(), strategyKey: args[2], ideaKey: args[3].toNumber(), stageKey: args[4].toNumber(), blockNumber: args[5].toNumber(), }, }) }) return ideaEvent$ } const readEvents = async ( contract: IContract, fromBlock?: number, toBlock?: number, ): Promise<any> => { const ideaFilter = contract.gate!.filters.IdeaCreated() // console.log('Querying the Mint events...') const mintEvents = await contract.gate!.queryFilter( ideaFilter, fromBlock, toBlock, ) // console.log('mintEvents') // console.log(mintEvents) return mintEvents } const getFirstBlock = async (contract: IContract): Promise<BigNumber> => { return contract.gate!.getFirstEventBlock() } const getLastBlock = async (contract: IContract): Promise<BigNumber> => { return contract.gate!.getLastEventBlock() } const listStrategies = async ( _creator: string, contract: IContract, ): Promise<Array<[string, string, string]>> => { const listStrategies = await contract.gate!.listStrategies(_creator) return listStrategies as Array<[string, string, string]> } const listAllStrategies = async ( contract: IContract, ): Promise<Array<[string, string, string]>> => { const listedAllStrategies = await contract.gate!.listAllStrategies() return listedAllStrategies as Array<[string, string, string]> } const listIdeas = async ( _creator: string, _strategyKey: string, contract: IContract, ): Promise<number[]> => { // log.dev('pre listIdeas:') const listedIdeas = await contract.gate!.listIdeas(_creator, _strategyKey) // log.dev('ends here') // log.dev('listedIdeas') // log.dev(listedIdeas) return listedIdeas.map((each: { toNumber: () => number }) => each.toNumber(), ) } const listStages = async ( _creator: string, _strategyKey: string, _ideaKey: number, contract: IContract, ): Promise<number[]> => { const listedStages = await floodProtectiveWrapper(async () => { return contract.gate!.listStages(_creator, _strategyKey, _ideaKey) }, 'listStages') return listedStages.map((each: { toNumber: () => number }) => each.toNumber(), ) } const getCreatorOfIdea = async ( nftIdea: number, contract: IContract, ): Promise<string> => { const creator = await contract.gate!.getCreatorOfNft(nftIdea) return creator.toString() } const getIdeaNftIdByKeys = async ( _creator: string, _strategyKey: string, _ideaKey: number, _ideaStageKey: number, contract: IContract, ): Promise<number> => { // provide contract return floodProtectiveWrapper(async () => { return ( (await contract.gate!.getIdeaByKeys( _creator, _strategyKey, _ideaKey, _ideaStageKey, )) as BigNumber ).toNumber() }, 'getIdeaNftIdByKeys') } const getIdeaMetadataUriByNftId = async ( ideaNft: number | BigNumber, contract: IContract, ): Promise<string> => { if (typeof ideaNft === 'number') { try { ideaNft = BigNumber.from(ideaNft.toString()) } catch (e) {} } let mid = await floodProtectiveWrapper( async () => { return contract.gate!.uri(Number(ideaNft)) }, 'getIdeaMetadataUriByNftId', 10, ) if (mid === 'https://ipfs.io/ipfs/') { throw new Error('NFT_NOT_FOUND') } return mid } const getMetadataIdByBlockId = async ( blockIdParam: number, contract: IContract, ): Promise<{ blockId: number metadataId: string nftId: number }> => { // console.log( // 'nft.module getMetadataIdByBlockId (blockIdParam)', // blockIdParam, // ) const data = await contract.gate!.getMetadataIdByBlockId(blockIdParam) if ([null, undefined, 'none'].includes(data[2])) { throw new Error('NFT MODULE ERROR: No metadata found for this block') } const blockId = data[0].toNumber() const nftId = data[1].toNumber() const metadataId = data[2] console.log('nft.module getMetadataIdByBlockId (blockId)', blockId) console.log('nft.module getMetadataIdByBlockId (nftId)', nftId) console.log('nft.module getMetadataIdByBlockId (metadataId)', metadataId) const response = { blockId, nftId, metadataId, } console.log('nft.module getMetadataIdByBlockId (response)', response) return response } const authorizeProvider = async (provider: string, contract: IContract) => { const auth = await contract.gate!.authorizeProvider(provider, { gasLimit: 2000000, }) await auth.wait() } const revokeProvider = async (provider: string, contract: IContract) => { const auth = await contract.gate!.revokeProvider(provider) await auth.wait() } /* * Provider check is supposed to be called by PROVIDER BACKEND to * test if he is authorized by client */ const providerCheck = async ( client: string, contract: IContract, ): Promise<boolean> => { const auth = await contract.gate!.providerCheck(client) return auth } /* * Authorize Check is supposed to be called by CLIENT ON BROWSER to * test if he authorized the provider */ const authorizeCheck = async ( provider: string, contract: IContract, ): Promise<boolean> => { const auth = await contract.gate!.authorizeCheck(provider) return auth } const getOwnedBy = async (owner: string, contract: IContract): Promise<any> => { const nfts = await contract.gate!.getNftsOwnedBy(owner) return nfts.map((each: { toNumber: () => any }) => each.toNumber()) } const getCreatedBy = async ( creator: string, contract: IContract, ): Promise<number[]> => { const nfts = await contract.gate!.getNftsCreatedBy(creator) return nfts.map((each: { toNumber: () => any }) => each.toNumber()) } const getLastId = async (contract: IContract): Promise<number> => { const lastNftId = await contract.gate!.getLastNftId() return lastNftId?.toNumber() } const floodProtectiveWrapper = async <T>( _f: () => Promise<T>, _fName: string, times: number = 10, ): Promise<T> => { let attempts = 0 let value: T | 'null' | undefined = 'null' const randomBetweenOneAnd20 = Math.floor(Math.random() * 20) + 1 await new Promise((resolve) => setTimeout(resolve, randomBetweenOneAnd20)) while (value === 'null') { try { value = await _f() } catch (err) { if (attempts >= times) { const errorParsed = (err as unknown as any).message || JSON.stringify(err) console.error(errorParsed) throw new Error( `Error in ${_fName} ` + times + ` times as logged.`, ) } const randomBetweenOneAnd20 = Math.floor(Math.random() * 20) + 1 await new Promise((resolve) => setTimeout(resolve, randomBetweenOneAnd20 * 10), ) } attempts++ } return value } export const NftModule = { /* MINT METHODS */ mint: { /* can be called by client or provider */ general: { createIdea, giveIdeaTo, }, /* by client user direct only */ clientOnly: { authorizeProvider, revokeProvider, }, }, /* VIEWS */ view: { /* can be called by client or provider */ general: { getFirstBlock, getLastBlock, watchEvents, readEvents, listStrategies, listAllStrategies, listIdeas, listStages, getCreatorOfIdea, getIdeaNftIdByKeys, getIdeaMetadataUriByNftId, getMetadataIdByBlockId, getOwnedBy, getCreatedBy, getLastId, getIdeaViewers, }, /* by provider only */ providerOnly: { providerCheck, }, /* by client only */ clientOnly: { authorizeCheck, }, }, }