diff --git a/.changeset/witty-maps-camp.md b/.changeset/witty-maps-camp.md new file mode 100644 index 00000000..a845151c --- /dev/null +++ b/.changeset/witty-maps-camp.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/CHANGELOG.md b/CHANGELOG.md index 8aa3763a..02d75c62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # @rosen-bridge/watcher +## 4.0.0 + +### Major Changes + +- Support contract and tokensMap version and update info controller for version configs +- Update watcher regarding to decimal drop refactor + +### Minor Changes + +- Add warn level to logger health parameter and tune the thresholds +- Add ethereum health check parameters +- Integrate Ethereum network + +### Patch Changes + +- Update scanner packages +- Always check ergo node sync status as a health parameter +- Log current watcher version +- Update significant decimal for tokens that are already stored in db +- Fix watcher network switch in scanner init + ## 3.2.2 ### Patch Changes diff --git a/config/default.yaml b/config/default.yaml index 3480f8bc..41048ca5 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -1,5 +1,5 @@ --- -network: '' # which scanner network used in this watcher ergo/cardano/bitcoin +network: '' # which scanner network used in this watcher ergo/cardano/bitcoin/ethereum observation: confirmation: 60 # number of required block confirmations to create the commitment after observing an event validThreshold: 12960 # observations that have not been triggered won't be processed after this period (in blocks) @@ -35,6 +35,14 @@ cardano: projectId: '' # blockfrost project Id timeout: 10 # blockfrost request timeout interval: 20 # blockfrost check timeout +ethereum: + type: '' # options: rpc + initial: + height: -1 # initial height of scanning + rpc: + url: '' # rpc url + interval: 20 # rpc scanning interval (in seconds) + timeout: 10 # rpc request timeout (in seconds) ergo: network: 'Mainnet' # ergo network type. testnet or mainnet type: 'node' # ergo scanner type. options: node, explorer @@ -93,11 +101,11 @@ healthCheck: ergWarnThreshold: 1000000000 # minimum recommended erg balance ergCriticalThreshold: 100000000 # minimum required erg balance ergoScanner: - warnDifference: 2 # warning difference between existing and scanned blocks height - criticalDifference: 10 # critical difference between existing and scanned blocks height + warnDifference: 5 # warning difference between existing and scanned blocks height + criticalDifference: 20 # critical difference between existing and scanned blocks height ergoNode: maxHeightDifference: 2 # maximum difference between header height and full height - maxBlockTime: 30 # maximum time to see a new block in minutes + maxBlockTime: 30 # maximum time to see a new block in minutes minPeerCount: 10 # minimum recommended peers maxPeerHeightDifference: 2 # maximum difference between peers and our node cardanoScanner: @@ -106,12 +114,16 @@ healthCheck: bitcoinScanner: warnDifference: 2 # warning difference between existing and scanned blocks height criticalDifference: 10 # critical difference between existing and scanned blocks height + ethereumScanner: + warnDifference: 40 # warning difference between existing and scanned blocks height + criticalDifference: 160 # critical difference between existing and scanned blocks height permit: warnCommitmentCount: 4 # warning remaining permits for creating commitment criticalCommitmentCount: 0 # critical remaining permits for creating commitment - errorLog: - maxAllowedCount: 2 # maximum allowed error log lines - duration: 100000 # error log duration time check in milliseconds + logs: + duration: 600 # log duration time check (in seconds) + maxAllowedErrorCount: 1 # maximum allowed error log lines + maxAllowedWarnCount: 10 # maximum allowed warn log lines redeemSwapEnabled: true # if set true when no permit left, system automatically redeem new commitments and commit again for old observations rewardCollection: threshold: 100000 # RSN threshold for reward collection diff --git a/docker/custom-environment-variables.yaml b/docker/custom-environment-variables.yaml index 710b2d4a..980bb157 100644 --- a/docker/custom-environment-variables.yaml +++ b/docker/custom-environment-variables.yaml @@ -15,4 +15,7 @@ cardano: authToken: 'KOIOS_AUTH_TOKEN' blockfrost: projectId: 'BLOCKFROST_PROJECT_ID' +ethereum: + rpc: + authToken: 'ETHEREUM_RPC_AUTH_TOKEN' overrideLokiBasicAuth: 'OVERRIDE_LOKI_BASIC_AUTH' diff --git a/package-lock.json b/package-lock.json index 3dc134a3..85345d0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@rosen-bridge/watcher", - "version": "3.2.2", + "version": "4.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@rosen-bridge/watcher", - "version": "3.2.2", + "version": "4.0.0", "hasInstallScript": true, "license": "ISC", "dependencies": { @@ -17,6 +17,8 @@ "@rosen-bridge/bitcoin-observation-extractor": "^4.0.7", "@rosen-bridge/bitcoin-rpc-scanner": "^0.2.8", "@rosen-bridge/discord-notification": "^0.1.3", + "@rosen-bridge/evm-observation-extractor": "^3.0.5", + "@rosen-bridge/evm-rpc-scanner": "^1.0.4", "@rosen-bridge/health-check": "6.0.3", "@rosen-bridge/log-level-check": "^1.0.3", "@rosen-bridge/minimum-fee": "^2.1.0", @@ -2195,6 +2197,33 @@ "node": ">=20.11.0" } }, + "node_modules/@rosen-bridge/evm-observation-extractor": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@rosen-bridge/evm-observation-extractor/-/evm-observation-extractor-3.0.5.tgz", + "integrity": "sha512-WJZdAKJHMtWXWXDDgo3Q3hZDZZxFWfSr8SuKDSvmIaOOxuCZ3VbKxHJlnOmz4LtxgncXK3YT0fdNqzQ1sOne9Q==", + "dependencies": { + "@rosen-bridge/abstract-logger": "^1.0.0", + "@rosen-bridge/evm-rpc-scanner": "^1.0.4", + "@rosen-bridge/observation-extractor": "^5.0.7", + "@rosen-bridge/rosen-extractor": "^6.2.0", + "@rosen-bridge/tokens": "^1.2.0", + "blakejs": "^1.2.1", + "ethers": "^6.11.0" + }, + "engines": { + "node": ">=20.11.0" + } + }, + "node_modules/@rosen-bridge/evm-rpc-scanner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@rosen-bridge/evm-rpc-scanner/-/evm-rpc-scanner-1.0.4.tgz", + "integrity": "sha512-IrMrpK51SJkgUT7QY/nfjb/1U4t9yh1C/x+eZeFe2vRlLN+kX9NUtYHwGCY3Qfnfd+zpFrbRQQ0ooG7C70H5FA==", + "dependencies": { + "@rosen-bridge/abstract-logger": "^1.0.0", + "@rosen-bridge/scanner": "^4.1.3", + "ethers": "^6.11.0" + } + }, "node_modules/@rosen-bridge/extended-typeorm": { "version": "0.0.3", "license": "GPL-3.0", diff --git a/package.json b/package.json index f686c150..7d6a5013 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rosen-bridge/watcher", - "version": "3.2.2", + "version": "4.0.0", "description": "Rosen bridge Watcher service", "main": "index.js", "type": "module", @@ -81,6 +81,8 @@ "@rosen-bridge/bitcoin-observation-extractor": "^4.0.7", "@rosen-bridge/bitcoin-rpc-scanner": "^0.2.8", "@rosen-bridge/discord-notification": "^0.1.3", + "@rosen-bridge/evm-observation-extractor": "^3.0.5", + "@rosen-bridge/evm-rpc-scanner": "^1.0.4", "@rosen-bridge/health-check": "6.0.3", "@rosen-bridge/log-level-check": "^1.0.3", "@rosen-bridge/minimum-fee": "^2.1.0", diff --git a/src/api/Transaction.ts b/src/api/Transaction.ts index c75ad356..418441ae 100644 --- a/src/api/Transaction.ts +++ b/src/api/Transaction.ts @@ -14,6 +14,8 @@ import { AddressBalance } from '../ergo/interfaces'; import { ChangeBoxCreationError, NoWID, NotEnoughFund } from '../errors/errors'; import { DetachWID } from '../transactions/detachWID'; import { + ERGO_CHAIN_NAME, + ERGO_NATIVE_ASSET, WID_LOCK_COUNT, WID_MINT_COUNT, WID_UNLOCK_COUNT, @@ -112,6 +114,7 @@ export class Transaction { /** * Get required permit count + * returns the wrapped amount */ getRequiredPermitsCountPerEvent = async () => { const configBox = await Transaction.boxes.getRepoConfigBox(); @@ -127,6 +130,7 @@ export class Transaction { /** * calculate total permit for current user + * returns the wrap amount * @returns */ getTotalPermit = async (): Promise => { @@ -165,8 +169,13 @@ export class Transaction { throw Error('one of registers (4, 5) of repo box is not set'); } + const tokenMap = getConfig().token.tokenMap; const collateralBox = await Transaction.boxes.getCollateralBox(wid); - const totalRwt = BigInt(collateralBox.register_value(5)!.to_i64().to_str()); + const totalRwt = tokenMap.unwrapAmount( + getConfig().rosen.RWTId, + BigInt(collateralBox.register_value(5)!.to_i64().to_str()), + ERGO_CHAIN_NAME + ).amount; const completeReturn = totalRwt == RWTCount; const inputBoxes = [repoBox, collateralBox, permitBoxes[0], widBox]; @@ -506,6 +515,7 @@ export class Transaction { /** * get Erg and RSN collateral + * returns the wrapped amount * CAUTION: this function removed in watcher refactor */ getCollateral = async () => { @@ -555,9 +565,18 @@ export class Transaction { status: 500, }; } + const tokenMap = getConfig().token.tokenMap; const collateral = await this.getCollateral(); - const ErgCollateral = collateral.erg; - const RSNCollateral = collateral.rsn; + const ErgCollateral = tokenMap.unwrapAmount( + ERGO_NATIVE_ASSET, + collateral.erg, + ERGO_CHAIN_NAME + ).amount; + const RSNCollateral = tokenMap.unwrapAmount( + getConfig().rosen.RSN, + collateral.rsn, + ERGO_CHAIN_NAME + ).amount; const inputBoxes = [repoBox]; const RSNTokenId = Transaction.RSN.to_str(); @@ -650,9 +669,11 @@ export class Transaction { ) ); } else { - const collateralRwtCount = BigInt( - collateralBox!.register_value(5)!.to_i64().to_str() - ); + const collateralRwtCount = tokenMap.unwrapAmount( + getConfig().rosen.RWTId, + BigInt(collateralBox!.register_value(5)!.to_i64().to_str()), + ERGO_CHAIN_NAME + ).amount; const rsnCollateral = ErgoUtils.getBoxAssetsSum([collateralBox!]).filter( (token) => token.tokenId == getConfig().rosen.RSN ); diff --git a/src/api/address.ts b/src/api/address.ts index 29b4c644..18080e49 100644 --- a/src/api/address.ts +++ b/src/api/address.ts @@ -1,14 +1,10 @@ import express, { Request, Response } from 'express'; import { getConfig } from '../config/config'; -import { validationResult, check } from 'express-validator'; +import { validationResult } from 'express-validator'; import { generateSK } from '../utils/utils'; import { ErgoUtils } from '../ergo/utils'; import { JsonBI } from '../ergo/network/parser'; -import { - ERGO_DECIMALS, - ERGO_NATIVE_ASSET, - ERGO_NATIVE_ASSET_NAME, -} from '../config/constants'; +import { ERGO_CHAIN_NAME, ERGO_NATIVE_ASSET } from '../config/constants'; import WinstonLogger from '@rosen-bridge/winston-logger'; const logger = WinstonLogger.getInstance().getLogger(import.meta.url); @@ -49,10 +45,24 @@ addressRouter.get('/assets', async (req: Request, res: Response) => { tokens.push({ amount: balance.nanoErgs, tokenId: ERGO_NATIVE_ASSET, - decimals: ERGO_DECIMALS, - name: ERGO_NATIVE_ASSET_NAME, + name: ERGO_NATIVE_ASSET, isNativeToken: true, }); + const tokenMap = getConfig().token.tokenMap; + tokens = tokens.map((token) => { + const wrappedToken = tokenMap.wrapAmount( + token.tokenId, + token.amount, + ERGO_CHAIN_NAME + ); + const significantDecimal = tokenMap.getSignificantDecimals(token.tokenId); + return { + ...token, + amount: wrappedToken.amount, + decimals: + significantDecimal != undefined ? significantDecimal : token.decimals, + }; + }); const { tokenId, tokenName, sortByAmount } = req.query; if (tokenId) { tokens = tokens.filter((token) => token.tokenId === (tokenId as string)); diff --git a/src/api/general.ts b/src/api/general.ts index e072cc52..56e177c9 100644 --- a/src/api/general.ts +++ b/src/api/general.ts @@ -6,11 +6,16 @@ import { HealthCheckSingleton } from '../../src/utils/healthCheck'; import { Transaction } from './Transaction'; import WinstonLogger from '@rosen-bridge/winston-logger'; import packageJson from '../../package.json' assert { type: 'json' }; +import { ERGO_CHAIN_NAME, ERGO_NATIVE_ASSET } from '../config/constants'; const logger = WinstonLogger.getInstance().getLogger(import.meta.url); interface GeneralInfo { - version: string; + versions: { + app: string; + contract: string; + tokensMap: string; + }; currentBalance: bigint; network: string; permitsPerEvent: bigint; @@ -38,15 +43,28 @@ const generalRouter = express.Router(); */ generalRouter.get('/', async (req: Request, res: Response) => { try { + const tokenMap = getConfig().token.tokenMap; const collateral = await Transaction.getInstance().getCollateral(); const info: GeneralInfo = { - version: packageJson.version, - currentBalance: (await ErgoUtils.getWatcherBalance()).nanoErgs, + versions: { + app: packageJson.version, + contract: getConfig().rosen.contractVersion, + tokensMap: getConfig().token.version, + }, + currentBalance: tokenMap.wrapAmount( + ERGO_NATIVE_ASSET, + (await ErgoUtils.getWatcherBalance()).nanoErgs, + ERGO_CHAIN_NAME + ).amount, network: getConfig().general.networkWatcher, permitsPerEvent: await Transaction.getInstance().getRequiredPermitsCountPerEvent(), permitCount: { - active: await ErgoUtils.getPermitCount(getConfig().rosen.RWTId), + active: tokenMap.wrapAmount( + getConfig().rosen.RWTId, + await ErgoUtils.getPermitCount(getConfig().rosen.RWTId), + ERGO_CHAIN_NAME + ).amount, total: await Transaction.getInstance().getTotalPermit(), }, health: { diff --git a/src/api/permit.ts b/src/api/permit.ts index 4d2b1ebf..95fd12f8 100644 --- a/src/api/permit.ts +++ b/src/api/permit.ts @@ -3,6 +3,8 @@ import { ApiResponse, Transaction } from './Transaction'; import { body, validationResult } from 'express-validator'; import WinstonLogger from '@rosen-bridge/winston-logger'; import { authenticateKey } from './authentication'; +import { getConfig } from '../config/config'; +import { ERGO_CHAIN_NAME } from '../config/constants'; const logger = WinstonLogger.getInstance().getLogger(import.meta.url); @@ -22,10 +24,15 @@ permitRouter.post( if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } - const RSNCount = req.body.count; + const tokenMap = getConfig().token.tokenMap; + const RSNCount = tokenMap.unwrapAmount( + getConfig().rosen.RSN, + BigInt(req.body.count), + ERGO_CHAIN_NAME + ).amount; const watcherTransaction = Transaction.getInstance(); const response: ApiResponse = await watcherTransaction.getPermit( - BigInt(RSNCount) + RSNCount ); if (response.status === 200) { res.status(200).send({ txId: response.response }); @@ -53,10 +60,15 @@ permitRouter.post( if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } - const RWTCount = req.body.count; + const tokenMap = getConfig().token.tokenMap; + const RWTCount = tokenMap.unwrapAmount( + getConfig().rosen.RWTId, + BigInt(req.body.count), + ERGO_CHAIN_NAME + ).amount; const watcherTransaction = Transaction.getInstance(); const response: ApiResponse = await watcherTransaction.returnPermit( - BigInt(RWTCount) + RWTCount ); if (response.status === 200) { res.status(200).send({ txId: response.response }); diff --git a/src/bootstrap.ts b/src/bootstrap.ts index e8339be7..4dd5a9bf 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -1,5 +1,12 @@ import 'reflect-metadata'; import WinstonLogger from '@rosen-bridge/winston-logger'; import { getConfig } from './config/config'; +import packageJson from '../package.json' assert { type: 'json' }; WinstonLogger.init(getConfig().logger.transports); + +const logger = WinstonLogger.getInstance().getLogger(import.meta.url); + +logger.info(`Watcher version: ${packageJson.version}`); +logger.info(`Watcher contract version: ${getConfig().rosen.contractVersion}`); +logger.info(`Watcher tokens version: ${getConfig().token.version}`); diff --git a/src/config/config.ts b/src/config/config.ts index 29340bf4..008cb8f9 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -16,12 +16,14 @@ const supportedNetworks: Array = [ Constants.ERGO_CHAIN_NAME, Constants.CARDANO_CHAIN_NAME, Constants.BITCOIN_CHAIN_NAME, + Constants.ETHEREUM_CHAIN_NAME, ]; interface ConfigType { logger: LoggerConfig; cardano: CardanoConfig; bitcoin: BitcoinConfig; + ethereum: EthereumConfig; general: Config; rosen: RosenConfig; token: TokensConfig; @@ -418,6 +420,38 @@ class BitcoinConfig { } } +class EthereumConfig { + type: string; + initialHeight: number; + rpc?: { + url: string; + timeout: number; + interval: number; + authToken?: string; + }; + + constructor(network: string) { + this.type = config.get('ethereum.type'); + if (network === Constants.ETHEREUM_CHAIN_NAME) { + this.initialHeight = getRequiredNumber('ethereum.initial.height'); + if (this.type == Constants.EVM_RPC_TYPE) { + const url = getRequiredString('ethereum.rpc.url'); + const timeout = getRequiredNumber('ethereum.rpc.timeout'); + const interval = getRequiredNumber('ethereum.rpc.interval'); + const authToken = getOptionalString( + 'ethereum.rpc.authToken', + undefined + ); + this.rpc = { url, timeout, interval, authToken }; + } else { + throw new Error( + `Improperly configured. ethereum configuration type is invalid available choices are '${Constants.EVM_RPC_TYPE}'` + ); + } + } + } +} + class DatabaseConfig { type: string; path = ''; @@ -482,6 +516,8 @@ class HealthCheckConfig { cardanoScannerCriticalDiff: number; bitcoinScannerWarnDiff: number; bitcoinScannerCriticalDiff: number; + ethereumScannerWarnDiff: number; + ethereumScannerCriticalDiff: number; ergoNodeMaxHeightDiff: number; ergoNodeMaxBlockTime: number; ergoNodeMinPeerCount: number; @@ -490,8 +526,9 @@ class HealthCheckConfig { permitCriticalCommitmentCount: number; permitDefaultCommitmentRWT: number; updateInterval: number; + logDuration: number; errorLogAllowedCount: number; - errorLogDuration: number; + warnLogAllowedCount: number; constructor() { this.ergWarnThreshold = BigInt( @@ -530,6 +567,12 @@ class HealthCheckConfig { this.bitcoinScannerCriticalDiff = getRequiredNumber( 'healthCheck.bitcoinScanner.criticalDifference' ); + this.ethereumScannerWarnDiff = getRequiredNumber( + 'healthCheck.ethereumScanner.warnDifference' + ); + this.ethereumScannerCriticalDiff = getRequiredNumber( + 'healthCheck.ethereumScanner.criticalDifference' + ); this.permitWarnCommitmentCount = getRequiredNumber( 'healthCheck.permit.warnCommitmentCount' ); @@ -537,10 +580,13 @@ class HealthCheckConfig { 'healthCheck.permit.criticalCommitmentCount' ); this.updateInterval = getRequiredNumber('healthCheck.interval'); + this.logDuration = getRequiredNumber('healthCheck.logs.duration') * 1000; this.errorLogAllowedCount = getRequiredNumber( - 'healthCheck.errorLog.maxAllowedCount' + 'healthCheck.logs.maxAllowedErrorCount' + ); + this.warnLogAllowedCount = getRequiredNumber( + 'healthCheck.logs.maxAllowedWarnCount' ); - this.errorLogDuration = getRequiredNumber('healthCheck.errorLog.duration'); } } @@ -552,6 +598,7 @@ const getConfig = (): ConfigType => { const logger = new LoggerConfig(); const cardano = new CardanoConfig(general.networkWatcher); const bitcoin = new BitcoinConfig(general.networkWatcher); + const ethereum = new EthereumConfig(general.networkWatcher); const rosen = new RosenConfig( general.networkWatcher, general.networkType, @@ -564,6 +611,7 @@ const getConfig = (): ConfigType => { internalConfig = { cardano, bitcoin, + ethereum, logger, general, rosen, diff --git a/src/config/constants.ts b/src/config/constants.ts index b188f0ac..31654f3e 100644 --- a/src/config/constants.ts +++ b/src/config/constants.ts @@ -5,6 +5,7 @@ export const TRIGGER_EXTRACTOR_NAME = 'watcher-trigger-extractor'; export const COLLATERAL_EXTRACTOR_NAME = 'watcher-collateral-extractor'; export const ESPLORA_TYPE = 'esplora'; export const RPC_TYPE = 'rpc'; +export const EVM_RPC_TYPE = 'rpc'; export const OGMIOS_TYPE = 'ogmios'; export const KOIOS_TYPE = 'koios'; export const BLOCK_FROST_TYPE = 'blockfrost'; @@ -13,13 +14,13 @@ export const EXPLORER_TYPE = 'explorer'; export const ERGO_CHAIN_NAME = 'ergo'; export const CARDANO_CHAIN_NAME = 'cardano'; export const BITCOIN_CHAIN_NAME = 'bitcoin'; +export const ETHEREUM_CHAIN_NAME = 'ethereum'; export const ERGO_NATIVE_ASSET = 'erg'; export const ERGO_DECIMALS = 9; export const DEFAULT_API_LIMIT = 20; export const MAX_API_LIMIT = 100; export const DOING_STATUS = 'Doing'; export const DONE_STATUS = 'Done'; -export const ERGO_NATIVE_ASSET_NAME = 'ERG'; export const WID_LOCK_COUNT = 2; export const WID_UNLOCK_COUNT = 2; export const WID_MINT_COUNT = 3; diff --git a/src/config/rosenConfig.ts b/src/config/rosenConfig.ts index 240dcdfd..5658e1b4 100644 --- a/src/config/rosenConfig.ts +++ b/src/config/rosenConfig.ts @@ -22,6 +22,7 @@ class RosenConfig { readonly emissionNFT: string; readonly emissionAddress: string; readonly eRSN: string; + readonly contractVersion: string; constructor(network: string, networkType: string, configRoot: string) { const rosenConfigPath = this.getAddress(network, networkType, configRoot); @@ -52,6 +53,7 @@ class RosenConfig { this.emissionNFT = config.tokens.EmissionNFT; this.eRSN = config.tokens.ERSN; this.emissionAddress = config.addresses.Emission; + this.contractVersion = config.version; } } diff --git a/src/config/tokensConfig.ts b/src/config/tokensConfig.ts index f9d61665..918f4978 100644 --- a/src/config/tokensConfig.ts +++ b/src/config/tokensConfig.ts @@ -4,13 +4,16 @@ import { RosenTokens, TokenMap } from '@rosen-bridge/tokens'; class TokensConfig { readonly tokens: RosenTokens; readonly tokenMap: TokenMap; + readonly version: string; constructor(tokensPath: string) { if (!fs.existsSync(tokensPath)) { throw new Error(`tokensMap file with path ${tokensPath} doesn't exist`); } else { const tokensJson: string = fs.readFileSync(tokensPath, 'utf8'); - this.tokens = JSON.parse(tokensJson); + const tokens = JSON.parse(tokensJson); + this.tokens = tokens; + this.version = tokens.version; this.tokenMap = new TokenMap(this.tokens); } } diff --git a/src/database/models/watcherModel.ts b/src/database/models/watcherModel.ts index a65e523f..1077c293 100644 --- a/src/database/models/watcherModel.ts +++ b/src/database/models/watcherModel.ts @@ -19,7 +19,11 @@ import { Not, Repository, } from 'typeorm'; -import { DOING_STATUS, DONE_STATUS } from '../../config/constants'; +import { + DOING_STATUS, + DONE_STATUS, + ERGO_CHAIN_NAME, +} from '../../config/constants'; import { PagedItemData } from '../../types/items'; import { EventStatus } from '../../utils/interfaces'; import { base64ToArrayBuffer } from '../../utils/utils'; @@ -34,6 +38,7 @@ import { RevenueView } from '../entities/revenueView'; import { TokenEntity } from '../entities/tokenEntity'; import { TxEntity, TxType } from '../entities/txEntity'; import WinstonLogger from '@rosen-bridge/winston-logger'; +import { getConfig } from '../../config/config'; const logger = WinstonLogger.getInstance().getLogger(import.meta.url); @@ -777,6 +782,7 @@ class WatcherDataBase { /** * Stores the name and decimals of a token by its id + * stores the significant decimals if exists in the tokenMap * @param tokenId * @param tokenName * @param decimals @@ -786,7 +792,13 @@ class WatcherDataBase { tokenName: string, decimals: number ) => { - await this.tokenRepository.insert({ tokenId, tokenName, decimals }); + const tokenMap = getConfig().token.tokenMap; + const significantDecimal = tokenMap.getSignificantDecimals(tokenId); + await this.tokenRepository.save({ + tokenId, + tokenName, + decimals: significantDecimal != undefined ? significantDecimal : decimals, + }); }; /** @@ -1096,6 +1108,7 @@ class WatcherDataBase { /** * Stores the info of permit in chart entity + * stores token's wrapped amount * @param tokenId * @param amount * @param permit @@ -1105,9 +1118,12 @@ class WatcherDataBase { amount: string, permit: PermitEntity ) => { + const tokenMap = getConfig().token.tokenMap; await this.revenueRepository.save({ tokenId, - amount, + amount: tokenMap + .wrapAmount(tokenId, BigInt(amount), ERGO_CHAIN_NAME) + .amount.toString(), permit, }); }; diff --git a/src/ergo/boxes.ts b/src/ergo/boxes.ts index 246f5bc1..c2127a69 100644 --- a/src/ergo/boxes.ts +++ b/src/ergo/boxes.ts @@ -22,6 +22,7 @@ import { AddressBalance } from './interfaces'; import { JsonBI } from './network/parser'; import WinstonLogger from '@rosen-bridge/winston-logger'; import { blake2b } from 'blakejs'; +import { ERGO_CHAIN_NAME } from '../config/constants'; const logger = WinstonLogger.getInstance().getLogger(import.meta.url); @@ -716,9 +717,15 @@ export class Boxes { 4, wasm.Constant.from_byte_array(Buffer.from(wid, 'hex')) ); + const tokenMap = getConfig().token.tokenMap; + const wrappedRwtCount = tokenMap.wrapAmount( + getConfig().rosen.RWTId, + rwtCount, + ERGO_CHAIN_NAME + ).amount; boxBuilder.set_register_value( 5, - wasm.Constant.from_i64(wasm.I64.from_str(rwtCount.toString())) + wasm.Constant.from_i64(wasm.I64.from_str(wrappedRwtCount.toString())) ); return boxBuilder.build(); }; diff --git a/src/ergo/utils.ts b/src/ergo/utils.ts index 9d23c049..ad7034f5 100644 --- a/src/ergo/utils.ts +++ b/src/ergo/utils.ts @@ -18,10 +18,9 @@ import { RevenueView } from '../database/entities/revenueView'; import { TokenEntity } from '../database/entities/tokenEntity'; import { RevenueEntity } from '../database/entities/revenueEntity'; import { - ERGO_DECIMALS, ERGO_NATIVE_ASSET, - ERGO_NATIVE_ASSET_NAME, ERGO_CHAIN_NAME, + ERGO_DECIMALS, } from '../config/constants'; import { PagedItemData } from '../types/items'; import { EventTriggerEntity } from '@rosen-bridge/watcher-data-extractor'; @@ -389,6 +388,7 @@ export class ErgoUtils { /** * fill token name and decimals for list of extracted tokens + * assume token amount is wrapped and should set significant decimals * @param tokens * @returns */ @@ -406,25 +406,25 @@ export class ErgoUtils { const tokenInfo = tokensInfoMap.get(token.tokenId); const name = token.tokenId === ERGO_NATIVE_ASSET - ? ERGO_NATIVE_ASSET_NAME - : tokenInfo?.tokenName || ''; + ? ERGO_NATIVE_ASSET + : tokenInfo?.tokenName; const decimals = token.tokenId === ERGO_NATIVE_ASSET ? ERGO_DECIMALS : tokenInfo?.decimals || 0; return { ...token, - name: name === '' ? undefined : name, + name: name, decimals: decimals, - isNativeToken: token.tokenId === ERGO_NATIVE_ASSET_NAME, + isNativeToken: token.tokenId === ERGO_NATIVE_ASSET, }; }); }; /** * Returns full token data by searching on token map + * use significant decimals * @param tokenId - * @param amount * @param chain * @returns TokenData */ @@ -437,8 +437,9 @@ export class ErgoUtils { let decimals = 0; let isNativeToken = false; if (tokenDetail.length) { + const significantDecimal = tokenMap.getSignificantDecimals(tokenId); name = tokenDetail[0][chain].name; - decimals = tokenDetail[0][chain].decimals; + decimals = significantDecimal || 0; isNativeToken = tokenDetail[0][chain].metaData.type === 'native'; } @@ -452,6 +453,7 @@ export class ErgoUtils { /** * Returns full token data by checking token details in database and tokenMap + * use significant decimals * @param tokenId */ static tokenDetail = async (tokenId: string) => { diff --git a/src/jobs/healthCheck.ts b/src/jobs/healthCheck.ts index 54a77f9c..1c808f81 100644 --- a/src/jobs/healthCheck.ts +++ b/src/jobs/healthCheck.ts @@ -2,6 +2,7 @@ import { getConfig } from '../config/config'; import { Boxes } from '../../src/ergo/boxes'; import { HealthCheckSingleton } from '../../src/utils/healthCheck'; import WinstonLogger from '@rosen-bridge/winston-logger'; +import { ERGO_CHAIN_NAME } from '../config/constants'; const logger = WinstonLogger.getInstance().getLogger(import.meta.url); @@ -18,7 +19,12 @@ const updatePermitCheckThreshold = async (boxes: Boxes) => { ); return; } - const commitmentRwt = BigInt(R4.to_i64_str_array()[0]); + const tokenMap = getConfig().token.tokenMap; + const commitmentRwt = tokenMap.unwrapAmount( + getConfig().rosen.RWTId, + BigInt(R4.to_i64_str_array()[0]), + ERGO_CHAIN_NAME + ).amount; if ( HealthCheckSingleton.getInstance().checkIfPermitCheckExists(commitmentRwt) ) { diff --git a/src/jobs/initScanner.ts b/src/jobs/initScanner.ts index 88cdc905..40cc764c 100644 --- a/src/jobs/initScanner.ts +++ b/src/jobs/initScanner.ts @@ -1,15 +1,17 @@ -import { - CardanoBlockFrostScanner, - CardanoKoiosScanner, - CardanoOgmiosScanner, - GeneralScanner, -} from '@rosen-bridge/scanner'; -import { BitcoinEsploraScanner } from '@rosen-bridge/bitcoin-esplora-scanner'; -import { BitcoinRpcScanner } from '@rosen-bridge/bitcoin-rpc-scanner'; +import { CardanoOgmiosScanner, GeneralScanner } from '@rosen-bridge/scanner'; import * as Constants from '../config/constants'; import { getConfig } from '../config/config'; import { scanner } from '../utils/scanner'; import WinstonLogger from '@rosen-bridge/winston-logger'; +import { EvmRpcScanner } from '@rosen-bridge/evm-rpc-scanner'; + +const allConfig = getConfig(); +const { + general: config, + cardano: cardanoConfig, + bitcoin: bitcoinConfig, + ethereum: ethereumConfig, +} = allConfig; const logger = WinstonLogger.getInstance().getLogger(import.meta.url); @@ -25,49 +27,44 @@ const scanningJob = async (interval: number, scanner: GeneralScanner) => { }; export const scannerInit = () => { - const allConfig = getConfig(); - const config = allConfig.general; scanningJob(config.ergoInterval, scanner.ergoScanner).then(() => null); - if (config.networkWatcher === Constants.CARDANO_CHAIN_NAME) { - const cardanoConfig = allConfig.cardano; - if (cardanoConfig.ogmios) { - (scanner.observationScanner as CardanoOgmiosScanner) - .start() - .catch((e) => { - logger.error(`Ogmios connection failed with error: ${e}`); - }); - } else if (cardanoConfig.koios) { - scanningJob( - cardanoConfig.koios.interval, - scanner.observationScanner as CardanoKoiosScanner - ).then(() => null); - } else if (cardanoConfig.blockfrost) { + switch (config.networkWatcher) { + case Constants.CARDANO_CHAIN_NAME: + if (cardanoConfig.ogmios) { + (scanner.observationScanner as CardanoOgmiosScanner) + .start() + .catch((e) => { + logger.error(`Ogmios connection failed with error: ${e}`); + }); + break; + } scanningJob( - cardanoConfig.blockfrost.interval, - scanner.observationScanner as CardanoBlockFrostScanner + cardanoConfig.koios + ? cardanoConfig.koios.interval + : cardanoConfig.blockfrost!.interval, + scanner.observationScanner as GeneralScanner ).then(() => null); - } else { - throw new Error( - `The observing network [${config.networkWatcher}] is not supported` - ); - } - } else if (config.networkWatcher === Constants.BITCOIN_CHAIN_NAME) { - const bitcoinConfig = allConfig.bitcoin; - if (bitcoinConfig.esplora) { + break; + case Constants.BITCOIN_CHAIN_NAME: scanningJob( - bitcoinConfig.esplora.interval, - scanner.observationScanner as BitcoinEsploraScanner + bitcoinConfig.rpc + ? bitcoinConfig.rpc.interval + : bitcoinConfig.esplora!.interval, + scanner.observationScanner as GeneralScanner ).then(() => null); - } else if (bitcoinConfig.rpc) { + break; + case Constants.ETHEREUM_CHAIN_NAME: scanningJob( - bitcoinConfig.rpc.interval, - scanner.observationScanner as BitcoinRpcScanner + ethereumConfig.rpc!.interval, + scanner.observationScanner as EvmRpcScanner ).then(() => null); - } else { + break; + case Constants.ERGO_CHAIN_NAME: + break; + default: throw new Error( `The observing network [${config.networkWatcher}] is not supported` ); - } } // TODO: Add commitment cleanup job diff --git a/src/jobs/tokenName.ts b/src/jobs/tokenName.ts index 96a1230b..86c12837 100644 --- a/src/jobs/tokenName.ts +++ b/src/jobs/tokenName.ts @@ -61,17 +61,12 @@ export const tokenNameJob = async (boxIds: string[]) => { const idKeyErgo = tokenMap.getIdKey(ERGO_CHAIN_NAME); for (const chain of chains) { const tokensData = tokenMap.getTokens(ERGO_CHAIN_NAME, chain); - const allTokenIds = tokensData.map((token) => token[idKeyErgo]); - const existingTokenIds = ( - await watcherDatabase.getTokenEntity(allTokenIds) - ).map((token) => token.tokenId); for (const tokenData of tokensData) { - if (!existingTokenIds.find((id) => id === tokenData[idKeyErgo])) - await watcherDatabase.insertTokenEntity( - tokenData[idKeyErgo], - tokenData.name, - tokenData.decimals - ); + await watcherDatabase.insertTokenEntity( + tokenData[idKeyErgo], + tokenData.name, + tokenData.decimals + ); } } initialized = true; diff --git a/src/transactions/commitmentCreation.ts b/src/transactions/commitmentCreation.ts index 7da8b121..b52e1aba 100644 --- a/src/transactions/commitmentCreation.ts +++ b/src/transactions/commitmentCreation.ts @@ -9,8 +9,8 @@ import { TxType } from '../database/entities/txEntity'; import { ObservationEntity } from '@rosen-bridge/observation-extractor'; import { TransactionUtils, WatcherUtils } from '../utils/watcherUtils'; import { getConfig } from '../config/config'; -import { DetachWID } from './detachWID'; import WinstonLogger from '@rosen-bridge/winston-logger'; +import { ERGO_CHAIN_NAME } from '../config/constants'; const logger = WinstonLogger.getInstance().getLogger(import.meta.url); @@ -56,9 +56,12 @@ export class CommitmentCreation { ) ); const repoConfigBox = await this.boxes.getRepoConfigBox(); - const requiredRWTCount = BigInt( - (repoConfigBox.register_value(4)?.to_js() as Array)[0] - ); + const tokenMap = getConfig().token.tokenMap; + const requiredRWTCount = tokenMap.unwrapAmount( + getConfig().rosen.RWTId, + BigInt((repoConfigBox.register_value(4)?.to_js() as Array)[0]), + ERGO_CHAIN_NAME + ).amount; const outCommitment = this.boxes.createCommitment( height, requiredRWTCount, diff --git a/src/transactions/commitmentRedeem.ts b/src/transactions/commitmentRedeem.ts index 63fd2f14..f5f25c9b 100644 --- a/src/transactions/commitmentRedeem.ts +++ b/src/transactions/commitmentRedeem.ts @@ -9,6 +9,7 @@ import { TxType } from '../database/entities/txEntity'; import { TransactionUtils, WatcherUtils } from '../utils/watcherUtils'; import { getConfig } from '../config/config'; import WinstonLogger from '@rosen-bridge/winston-logger'; +import { ERGO_CHAIN_NAME } from '../config/constants'; const logger = WinstonLogger.getInstance().getLogger(import.meta.url); @@ -197,9 +198,15 @@ export class CommitmentRedeem { logger.debug('Has unconfirmed commitment or redeem transactions'); return; } + const tokenMap = getConfig().token.tokenMap; + const rwtPerCommitment = tokenMap.unwrapAmount( + getConfig().rosen.RWTId, + await Transaction.getInstance().getRequiredPermitsCountPerEvent(), + ERGO_CHAIN_NAME + ).amount; const availablePermits = ((await ErgoUtils.getPermitCount(getConfig().rosen.RWTId)) - 1n) / - (await Transaction.getInstance().getRequiredPermitsCountPerEvent()); + rwtPerCommitment; if (availablePermits >= 1) { logger.debug( `Still have [${availablePermits}] available permits, aborting last commit redeem job` diff --git a/src/transactions/commitmentReveal.ts b/src/transactions/commitmentReveal.ts index ad68efda..0789c1f0 100644 --- a/src/transactions/commitmentReveal.ts +++ b/src/transactions/commitmentReveal.ts @@ -10,6 +10,7 @@ import { ObservationEntity } from '@rosen-bridge/observation-extractor'; import { TransactionUtils, WatcherUtils } from '../utils/watcherUtils'; import { getConfig } from '../config/config'; import WinstonLogger from '@rosen-bridge/winston-logger'; +import { ERGO_CHAIN_NAME } from '../config/constants'; const logger = WinstonLogger.getInstance().getLogger(import.meta.url); diff --git a/src/types/config.ts b/src/types/config.ts index a641f7a0..b72dcef5 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -3,6 +3,7 @@ import * as Constants from '../config/constants'; type NetworkType = | typeof Constants.CARDANO_CHAIN_NAME | typeof Constants.ERGO_CHAIN_NAME - | typeof Constants.BITCOIN_CHAIN_NAME; + | typeof Constants.BITCOIN_CHAIN_NAME + | typeof Constants.ETHEREUM_CHAIN_NAME; export { NetworkType }; diff --git a/src/utils/healthCheck.ts b/src/utils/healthCheck.ts index 1ad8a3e4..b19e9d73 100644 --- a/src/utils/healthCheck.ts +++ b/src/utils/healthCheck.ts @@ -18,6 +18,7 @@ import { ErgoNodeScannerHealthCheck, BitcoinEsploraScannerHealthCheck, BitcoinRPCScannerHealthCheck, + EthereumRPCScannerHealthCheck, } from '@rosen-bridge/scanner-sync-check'; import { ExplorerWidHealthCheckParam, @@ -35,6 +36,7 @@ import { ERGO_DECIMALS, ERGO_NATIVE_ASSET, ESPLORA_TYPE, + ETHEREUM_CHAIN_NAME, EXPLORER_TYPE, KOIOS_TYPE, NODE_TYPE, @@ -74,7 +76,7 @@ class HealthCheckSingleton { notificationCheckConfig: { hasBeenUnstableForAWhile: { windowDuration: - getConfig().notification.hasBeenUnknownForAWhileWindowDuration, + getConfig().notification.hasBeenUnstableForAWhileWindowDuration, }, hasBeenUnknownForAWhile: { windowDuration: @@ -89,14 +91,32 @@ class HealthCheckSingleton { } this.healthCheck = new HealthCheck(notify, notificationConfig); const errorLogHealthCheck = new LogLevelHealthCheck( - logger, + WinstonLogger.getInstance().getDefaultLogger(), HealthStatusLevel.UNSTABLE, getConfig().healthCheck.errorLogAllowedCount, - getConfig().healthCheck.errorLogDuration, + getConfig().healthCheck.logDuration, 'error' ); this.healthCheck.register(errorLogHealthCheck); + const warnLogHealthCheck = new LogLevelHealthCheck( + WinstonLogger.getInstance().getDefaultLogger(), + HealthStatusLevel.UNSTABLE, + getConfig().healthCheck.warnLogAllowedCount, + getConfig().healthCheck.logDuration, + 'warn' + ); + this.healthCheck.register(warnLogHealthCheck); + + const ergoNodeSyncCheck = new ErgoNodeSyncHealthCheckParam( + getConfig().healthCheck.ergoNodeMaxHeightDiff, + getConfig().healthCheck.ergoNodeMaxBlockTime, + getConfig().healthCheck.ergoNodeMinPeerCount, + getConfig().healthCheck.ergoNodeMaxPeerHeightDifference, + getConfig().general.nodeUrl + ); + this.healthCheck.register(ergoNodeSyncCheck); + if (getConfig().general.scannerType === NODE_TYPE) { this.registerErgoNodeHealthCheckParams(); } else if (getConfig().general.scannerType === EXPLORER_TYPE) { @@ -108,6 +128,9 @@ class HealthCheckSingleton { if (getConfig().general.networkWatcher === BITCOIN_CHAIN_NAME) { this.registerBitcoinHealthCheckParams(); } + if (getConfig().general.networkWatcher === ETHEREUM_CHAIN_NAME) { + this.registerEthereumHealthCheckParams(); + } } private observingNetworkLastBlock = (scanner: string) => { @@ -145,15 +168,6 @@ class HealthCheckSingleton { getConfig().general.nodeUrl ); this.healthCheck.register(this.ergoScannerSyncCheckParam); - - const ergoNodeSyncCheck = new ErgoNodeSyncHealthCheckParam( - getConfig().healthCheck.ergoNodeMaxHeightDiff, - getConfig().healthCheck.ergoNodeMaxBlockTime, - getConfig().healthCheck.ergoNodeMinPeerCount, - getConfig().healthCheck.ergoNodeMaxPeerHeightDifference, - getConfig().general.nodeUrl - ); - this.healthCheck.register(ergoNodeSyncCheck); }; /** @@ -250,6 +264,22 @@ class HealthCheckSingleton { this.healthCheck.register(bitcoinScannerSyncCheck); }; + /** + * Registers all ethereum check params + */ + registerEthereumHealthCheckParams = () => { + const ethereumRpcScannerSyncCheck = new EthereumRPCScannerHealthCheck( + this.observingNetworkLastBlock(scanner.observationScanner.name()), + scanner.observationScanner.name(), + getConfig().healthCheck.ethereumScannerWarnDiff, + getConfig().healthCheck.ethereumScannerCriticalDiff, + getConfig().ethereum.rpc!.url, + getConfig().ethereum.rpc!.authToken, + getConfig().ethereum.rpc!.timeout + ); + this.healthCheck.register(ethereumRpcScannerSyncCheck); + }; + /** * Registers permit check if watcher wid exists */ diff --git a/src/utils/scanner.ts b/src/utils/scanner.ts index e1efa8bd..2cbf0431 100644 --- a/src/utils/scanner.ts +++ b/src/utils/scanner.ts @@ -29,6 +29,8 @@ import WinstonLogger from '@rosen-bridge/winston-logger'; import { getConfig } from '../config/config'; import { dataSource } from '../../config/dataSource'; import * as Constants from '../config/constants'; +import { EvmRpcScanner } from '@rosen-bridge/evm-rpc-scanner'; +import { EthereumRpcObservationExtractor } from '@rosen-bridge/evm-observation-extractor'; const allConfig = getConfig(); const { @@ -37,6 +39,7 @@ const { rosen: rosenConfig, cardano: cardanoConfig, bitcoin: bitcoinConfig, + ethereum: ethereumConfig, } = allConfig; /** @@ -74,14 +77,22 @@ class CreateScanner { | CardanoOgmiosScanner | CardanoBlockFrostScanner | BitcoinEsploraScanner - | BitcoinRpcScanner; + | BitcoinRpcScanner + | EvmRpcScanner; constructor() { this.createErgoScanner(); - if (config.networkWatcher === Constants.CARDANO_CHAIN_NAME) - this.createCardanoScanner(); - else if (config.networkWatcher === Constants.BITCOIN_CHAIN_NAME) - this.createBitcoinScanner(); + switch (config.networkWatcher) { + case Constants.BITCOIN_CHAIN_NAME: + this.createBitcoinScanner(); + break; + case Constants.CARDANO_CHAIN_NAME: + this.createCardanoScanner(); + break; + case Constants.ETHEREUM_CHAIN_NAME: + this.createEthereumScanner(); + break; + } if (!this.observationScanner) throw Error( 'Observation scanner initialization failed, check the watcher network to be correct' @@ -276,6 +287,32 @@ class CreateScanner { } } }; + + private createEthereumScanner = () => { + if (!this.observationScanner) { + if (ethereumConfig.rpc) { + this.observationScanner = new EvmRpcScanner( + Constants.ETHEREUM_CHAIN_NAME, + { + RpcUrl: ethereumConfig.rpc.url, + timeout: ethereumConfig.rpc.timeout * 1000, + initialHeight: ethereumConfig.initialHeight, + dataSource: dataSource, + }, + loggers.scannerLogger, + ethereumConfig.rpc.authToken + ); + + const observationExtractor = new EthereumRpcObservationExtractor( + rosenConfig.lockAddress, + dataSource, + tokensConfig.tokens, + loggers.observationExtractorLogger + ); + this.observationScanner.registerExtractor(observationExtractor); + } + } + }; } const scanner = new CreateScanner(); diff --git a/tests/config/contracts-ergo-mainnet.json b/tests/config/contracts-ergo-mainnet.json index 91bc26a3..bb64a916 100644 --- a/tests/config/contracts-ergo-mainnet.json +++ b/tests/config/contracts-ergo-mainnet.json @@ -1,4 +1,5 @@ { + "version": "testVersion", "addresses": { "RWTRepo": "5qwczr7KdspMpxJYy77vzNv3a4qT2x1VuDj8u9y5ozV3AAQtESPpbsR1EMWATVDdj6jguyC8B8NmVzami197gVHH4dhLvzqTye236iqBf75rGzF6wykBFvhoR9ZAbbzo4nz9P76U66ShQiwP17LvKiQMtQbwnLjDUtb8YifJYgAU6DEDm66RMKqisuFgxJ5D4y2LxiMLyKWPaHBdccEKqDYeTwhiPqwkhhsU5MsTKkoS43jK6s7xCFtFDxYFN3WCpaP2qffQqKvdFxaPp91UwHSBLzfRQsjq5hiMj7XnqKR2yUKdgEaY5VHLGY4opjgqnzPVBH28U3K6xpMxNgtg2W3siLLXqehST1YbAAx5rqyWgFhoKELWvoWaEDFUg1wQVLxteUJnNaLxEms2ZQnrRph5FVPAk9tfDhhsGzFMaGZzzuykWnEjGWYhKZxNMWJ4wyLijN1s2AoMj163G86fyZgN578ojpJZWRoT1ZBuPszoiFchP8Nko69cDjsPrbyiEr3iv78d3hkAGZEswFqnGkAXa7pdqUzDGCHteau9NFFMXvf5VhVwpVGwXHXXJFZC738AjC6W7GrGyMb9iDDtEWZ9xUnviCW8pVXHvB9enJ3TdsCYYwiUuCLWDbAkwmDDiQ9bWgY4evWWkpHzBZL8h4McUp8GFmUgwG4Y9bR57exyDNKDXMo2KzumNf9h18w8FeLk9R5F6zvqBwevcGWmzkrCAbeLs1btUhjZ5BoXeCAzBaT6eTMpRenffe3mEEW3h2EPgTuacZ4GKZ5mu8MLZxMFNo9PBEncekcBt5UetijiHuwQgy9Limg19xUSYRKmZyU9wfSHswEBGvRZczSXSPsopLb9B9srsZMHCPUxapmNxtXZP2kRQSxbnQV7QdVRD2C6PiyMKKqQGnk77SNpQsJ6VesqqRoJncxiz6GGVs1R4Kt4zwrZVFSjyGDXZoooFzFieUB6SLwWmCFyo3nF4SfWDhfPWQhdjycWbmMRUTPt9iuwGedDYBzfZnjLFSbiNJwYSZqv9UidfQMv8KmM9R4uaenFGJK5ct6Vt9Bnny7Br1KHJL9CDv1iKDtJMZyQXzUWYAn9MpCiGam7GDt6jJcRN8P68ssxd8dzSHTQNkM8TQfaxpXdMgx8G6RsQWQSjcraHKQmPG4kgcWAV9DJKQFUUTGfnEMbwkp7UCuU38j3hMaDMX6uxV6bXnVUexs4CwhkcpUvp6Znve6qhE2Fj183DuVrPnyEyZ69wVaDUyFURhSy6vzbNAp1rCKn26X8VGjiPSsm3VBQQXXmgWkc9NN5xijjh6hP4XJw3dsUqowP3FrEbokk5u3AzqQege1F6C3SqtyjmwyCwEurYafxqmyad9S2x9tXis1TyDo2KKG4vAWixgGfHLevhZGiPpUfFpsS23iyLxDt6diPdfDp", "WatcherPermit": "FxMs5S8B9DA3Ecw7taWSSRiaDkbYxxqsrwQKJVyJAbe7tBqLU4j3ry1En9AHn2d2XWEyj46FTHXM3RwxpLztvSJVQt76JY5jq3MF8TA9aWp2i134zDM8mm3zXH5RJXvRi7uit4QLHXhXvMp66fj9UaeK2UQSBVGXQbPKm9KkCCaVX66tLbJ3FFo58atK8VTvpFUfCPjLFn8U9sukxvfrVQwFFXSG753VLKFLzw614gmBrZFg6recogxyxuRSjCa42baii5i3cKTmwSARD38kFB1mftWGTUPRbV6kq3cz17CUtbCun6zKVFDqAMAKttEfbzknafAnudYVs1ijdc5kvAjDyWRaYVbjQt43oHhFGFpUrP9gae5HqrVpEJvcE551iV4L2yRfy35FdvPSTevkweANuZE58GA8i684FTQi7yWgRXRyaRsro4zXoXpgoizUJBCy76QUm27FN7bpHTH6GHRs3PW4x59Pu1BcdBMKz3yixyYK5eoxDau22a12bT", diff --git a/tests/config/tokens.test.json b/tests/config/tokens.test.json index e9e8d2c6..0beb7641 100644 --- a/tests/config/tokens.test.json +++ b/tests/config/tokens.test.json @@ -1,4 +1,5 @@ { + "version": "testVersion", "idKeys": { "ergo": "tokenId", "cardano": "tokenId" @@ -118,6 +119,29 @@ "residency": "native" } } + }, + { + "ergo": { + "tokenId": "erg", + "name": "test token 12", + "decimals": 9, + "metaData": { + "type": "native", + "residency": "native" + } + }, + "cardano": { + "tokenId": "policy_id4.123", + "policyId": "policy_id4", + "assetName": "123", + "unit": "", + "name": "cardano wrapped erg", + "decimals": 9, + "metaData": { + "type": "CIP26", + "residency": "native" + } + } } ] } diff --git a/tests/database/mockedData.ts b/tests/database/mockedData.ts index 40b2d6e3..30188bb7 100644 --- a/tests/database/mockedData.ts +++ b/tests/database/mockedData.ts @@ -17,6 +17,7 @@ import { TokenEntity } from '../../src/database/entities/tokenEntity'; import { RevenueEntity } from '../../src/database/entities/revenueEntity'; import { firstPermit, secondPermit } from '../ergo/statistics/mockUtils'; import packageJson from '../../package.json' assert { type: 'json' }; +import { getConfig } from '../../src/config/config'; export const ergoBlockEntity = new BlockEntity(); ergoBlockEntity.scanner = Constants.ERGO_CHAIN_NAME; @@ -174,9 +175,9 @@ export const validBox1Token = { export const validTwoBoxErgAmount = { amount: 1861100000, tokenId: Constants.ERGO_NATIVE_ASSET, - decimals: Constants.ERGO_DECIMALS, - name: Constants.ERGO_NATIVE_ASSET_NAME, + name: Constants.ERGO_NATIVE_ASSET, isNativeToken: true, + decimals: Constants.ERGO_DECIMALS, }; export const eventTriggerEntity = new EventTriggerEntity(); @@ -718,7 +719,11 @@ export const permitMockRWT = '8e5b02ba729ad364867619d2a8b9ff1438190c14979a12aa0a249e996194f074'; export const generalInfo = { - version: packageJson.version, + versions: { + app: packageJson.version, + contract: getConfig().rosen.contractVersion, + tokensMap: getConfig().token.version, + }, currentBalance: 1100000, network: 'ergo', permitsPerEvent: 10000, diff --git a/tests/database/watcherDatabase.ts b/tests/database/watcherDatabase.ts index 561681ea..581055b4 100644 --- a/tests/database/watcherDatabase.ts +++ b/tests/database/watcherDatabase.ts @@ -662,7 +662,7 @@ describe('WatcherModel tests', () => { it('should insert token record successfully', async () => { // mock a new tokenRecord const tokenRecord2 = new TokenEntity(); - tokenRecord2.tokenId = 'tokenId2'; + tokenRecord2.tokenId = 'tokenId22'; tokenRecord2.tokenName = 'tokenName2'; tokenRecord2.decimals = 0;