diff --git a/config/default.json5 b/config/default.json5 index 2a6f3838..f6017e3f 100644 --- a/config/default.json5 +++ b/config/default.json5 @@ -41,6 +41,7 @@ // Sets if Storage service should be enabled enabled: true, + // Storage Manager Contract storageManager: { // Topics that will be listened to topics: [ @@ -69,12 +70,49 @@ confirmations: 6 }, + // Specify behavior of NewBlockEmitter, that detects new blocks on blockchain. + newBlockEmitter: { + // If to use polling strategy, if false then listening is used. + polling: true + } + }, + + // Staking Contract + staking: { + // Topics that will be listened to + topics: [ + 'TotalCapacitySet(address,uin64)', + 'BillingPlanSet(address,uint64,uint128)', + 'MessageEmitted(address,bytes32[])', + 'NewAgreement(bytes32,bytes32[],address,address,uint128,uint64,uint128,uint256)', + 'AgreementFundsDeposited(bytes32,uint256)', + 'AgreementFundsWithdrawn(bytes32,uint256)', + 'AgreementFundsPayout(bytes32,uint256)', + 'AgreementStopped(bytes32)', + ], + + // Specify behavior of EventsEmitter, that retrieves events from blockchain and pass them onwards for further processing. + eventsEmitter: { + // If to use polling strategy, if false then listening is used. + polling: true, + + // Interval in milliseconds, how often is blockchain checked. + pollingInterval: 5000, + + // Starting block that upon first start of the service, will the blockchain be crawled for the past events. + startingBlock: "genesis", + + // Number of blocks that will be waited before passing an event for further processing. + confirmations: 6 + }, + // Specify behavior of NewBlockEmitter, that detects new blocks on blockchain. newBlockEmitter: { // If to use polling strategy, if false then listening is used. polling: true } } + }, // Settings for RNS service related function diff --git a/package-lock.json b/package-lock.json index 3b0703f7..141a4f84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3210,9 +3210,8 @@ } }, "@rsksmart/rif-marketplace-storage": { - "version": "0.1.0-dev.1", - "resolved": "https://registry.npmjs.org/@rsksmart/rif-marketplace-storage/-/rif-marketplace-storage-0.1.0-dev.1.tgz", - "integrity": "sha512-PxVbJ0pTAQugo3tSzUE6IjzcWPmHWkaX27Tsbl0OdMPZ2Z05fPc4a8rqzBazYDa4YdxA31voKE11KOPsiW38mw==" + "version": "git+https://github.com/rsksmart/rif-marketplace-storage.git#b79fde0659a8e6b588aec665bf2c857f1bf7b9a3", + "from": "git+https://github.com/rsksmart/rif-marketplace-storage.git#feat/staking-multi-currency" }, "@rsksmart/rns-auction-registrar": { "version": "1.0.2", diff --git a/package.json b/package.json index a0adf2e6..10a940b5 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@oclif/parser": "^3.8.5", "@oclif/plugin-help": "^3.2.0", "@rsksmart/rif-marketplace-nfts": "~0.1.2", - "@rsksmart/rif-marketplace-storage": "^0.1.0-dev.1", + "@rsksmart/rif-marketplace-storage": "https://github.com/rsksmart/rif-marketplace-storage.git#feat/staking-multi-currency", "@rsksmart/rns-auction-registrar": "1.0.2", "@rsksmart/rns-reverse": "^1.0.2", "@rsksmart/rns-rskregistrar": "^1.2.1", diff --git a/src/services/storage/handlers/stake.ts b/src/services/storage/handlers/stake.ts index 8aed9560..3448b13e 100644 --- a/src/services/storage/handlers/stake.ts +++ b/src/services/storage/handlers/stake.ts @@ -11,19 +11,41 @@ const handlers = { async Staked (event: EventData, { stakeService }: StorageServices): Promise { const { user: account, total, token, amount } = event.returnValues - const [, [stake]] = await StakeModel.update({ total }, { where: { account, tokenAddress: token } }) - logger.info(`Account ${account} stake amount ${amount}, final balance ${total}`) + const stakeData = { + total, + token, + account + } + await StakeModel.upsert(stakeData) + const stake = await StakeModel.findOne({ where: { token } }) + + if (!stake) { + throw new Error(`Stake for token ${stakeData.token} was not created`) + } - if (stakeService.emit) stakeService.emit('updated', stake.toJSON()) + logger.info(`Account ${stake?.account} stake amount ${amount}, final balance ${stake?.total}`) + + if (stakeService.emit) stakeService.emit('updated', stake!.toJSON()) }, async Unstaked (event: EventData, { stakeService }: StorageServices): Promise { const { user: account, total, token, amount } = event.returnValues - const [, [stake]] = await StakeModel.update({ total }, { where: { account, tokenAddress: token } }) - logger.info(`Account ${account} unstack amount ${amount}, final balance ${total}`) + const stakeData = { + total, + token, + account + } + await StakeModel.upsert(stakeData) + const stake = await StakeModel.findOne({ where: { token } }) - if (stakeService.emit) stakeService.emit('updated', stake.toJSON()) + if (!stake) { + throw new Error(`Stake for token ${stakeData.token} was not created`) + } + + logger.info(`Account ${stake?.account} unstack amount ${amount}, final balance ${stake?.total}`) + + if (stakeService.emit) stakeService.emit('updated', stake?.toJSON()) } } @@ -41,4 +63,5 @@ const handler: Handler = { return handlers[event.event](event, services) } } + export default handler diff --git a/src/services/storage/index.ts b/src/services/storage/index.ts index d4e91251..4b9c141f 100644 --- a/src/services/storage/index.ts +++ b/src/services/storage/index.ts @@ -1,10 +1,12 @@ import storageManagerContract from '@rsksmart/rif-marketplace-storage/build/contracts/StorageManager.json' +import stakingContract from '@rsksmart/rif-marketplace-storage/build/contracts/Staking.json' import config from 'config' import { Service } from 'feathers-sequelize' import { getObject } from 'sequelize-store' import Eth from 'web3-eth' import { EventData } from 'web3-eth-contract' import { AbiItem } from 'web3-utils' +import { EventEmitter } from 'events' import { ethFactory } from '../../blockchain' import { getEventsEmitterForService, isServiceInitialized } from '../../blockchain/utils' @@ -41,27 +43,18 @@ export interface StorageServices { } const SERVICE_NAME = 'storage' +const STORAGE_MANAGER = 'storageManager' +const STAKING = 'staking' const logger = loggingFactory(SERVICE_NAME) -// TODO add staking to precache -function precache (possibleEth?: Eth): Promise { +function precacheContract (eventEmitter: EventEmitter, services: StorageServices, eth: Eth): Promise { return new Promise((resolve, reject) => { - const eth = possibleEth || ethFactory() - const eventsEmitter = getEventsEmitterForService(`${SERVICE_NAME}.storageManager`, eth, storageManagerContract.abi as AbiItem[]) - // const eventsEmitter = getEventsEmitterForService(`${SERVICE_NAME}.stake`, eth, storageManagerContract.abi as AbiItem[]) - const dataQueue: EventData[] = [] const dataQueuePusher = (event: EventData): void => { dataQueue.push(event) } - const services: StorageServices = { - stakeService: new StakeService({ Model: StakeModel }), - offerService: new OfferService({ Model: Offer }), - agreementService: new AgreementService({ Model: Agreement }) - } - - eventsEmitter.on('initFinished', async () => { - eventsEmitter.off('newEvent', dataQueuePusher) + eventEmitter.on('initFinished', async () => { + eventEmitter.off('newEvent', dataQueuePusher) // Needs to be sequentially processed const processor = eventProcessor(services, eth) @@ -74,13 +67,31 @@ function precache (possibleEth?: Eth): Promise { reject(e) } }) - eventsEmitter.on('newEvent', dataQueuePusher) - eventsEmitter.on('error', (e: Error) => { + eventEmitter.on('newEvent', dataQueuePusher) + eventEmitter.on('error', (e: Error) => { logger.error(`There was unknown error in Events Emitter! ${e}`) }) }) } +async function precache (possibleEth?: Eth): Promise { + const eth = possibleEth || ethFactory() + const storageEventsEmitter = getEventsEmitterForService(`${SERVICE_NAME}.${STORAGE_MANAGER}`, eth, storageManagerContract.abi as AbiItem[]) + const stakingEventsEmitter = getEventsEmitterForService(`${SERVICE_NAME}.${STAKING}`, eth, stakingContract.abi as AbiItem[]) + + const services: StorageServices = { + stakeService: new StakeService({ Model: StakeModel }), + offerService: new OfferService({ Model: Offer }), + agreementService: new AgreementService({ Model: Agreement }) + } + + // Precache Storage Manager + await precacheContract(storageEventsEmitter, services, eth) + + // Precache Staking + await precacheContract(stakingEventsEmitter, services, eth) +} + const storage: CachedService = { async initialize (app: Application): Promise { if (!config.get('storage.enabled')) { @@ -119,7 +130,7 @@ const storage: CachedService = { const confirmationService = app.service(ServiceAddresses.CONFIRMATIONS) // Storage Manager watcher - const storageManagerEventsEmitter = getEventsEmitterForService(`${SERVICE_NAME}.storageManager`, eth, storageManagerContract.abi as AbiItem[]) + const storageManagerEventsEmitter = getEventsEmitterForService(`${SERVICE_NAME}.${STORAGE_MANAGER}`, eth, storageManagerContract.abi as AbiItem[]) storageManagerEventsEmitter.on('newEvent', errorHandler(eventProcessor(services, eth), logger)) storageManagerEventsEmitter.on('error', (e: Error) => { logger.error(`There was unknown error in Events Emitter! ${e}`) @@ -128,7 +139,7 @@ const storage: CachedService = { storageManagerEventsEmitter.on('invalidConfirmation', (data) => confirmationService.emit('invalidConfirmation', data)) // Staking watcher - const stakingEventsEmitter = getEventsEmitterForService(`${SERVICE_NAME}.staking`, eth, stakingContract.abi as AbiItem[]) + const stakingEventsEmitter = getEventsEmitterForService(`${SERVICE_NAME}.${STAKING}`, eth, stakingContract.abi as AbiItem[]) stakingEventsEmitter.on('newEvent', errorHandler(eventProcessor(services, eth), logger)) stakingEventsEmitter.on('error', (e: Error) => { logger.error(`There was unknown error in Events Emitter! ${e}`) diff --git a/src/services/storage/models/stake.model.ts b/src/services/storage/models/stake.model.ts index 5a4d8c10..85a188bd 100644 --- a/src/services/storage/models/stake.model.ts +++ b/src/services/storage/models/stake.model.ts @@ -9,10 +9,10 @@ export default class StakeModel extends Model { total!: BigNumber @Column - tokenName!: string + tokenName?: string @Column - tokenAddress?: string + token!: string @Column account!: string