diff --git a/.changeset/firo-asset-calculator.md b/.changeset/firo-asset-calculator.md new file mode 100644 index 000000000..0f1efcf18 --- /dev/null +++ b/.changeset/firo-asset-calculator.md @@ -0,0 +1,11 @@ +--- +'@rosen-ui/asset-calculator': minor +'@rosen-ui/constants': minor +--- + +Add Firo chain support to asset-calculator + +- Register `firo` in the `NETWORKS` constant (no token support, native token `firo`). +- Add `FiroCalculator` that reads locked amounts from the Firo insight-api + (`/insight-api-zcoin/addr/{address}/balance`). +- Add `FiroCalculatorInterface` and register `FiroCalculator` in `AssetCalculator`. diff --git a/.lintstagedrc.mjs b/.lintstagedrc.mjs index 046e4cb37..c46fa658e 100644 --- a/.lintstagedrc.mjs +++ b/.lintstagedrc.mjs @@ -21,8 +21,8 @@ const perPackage = (resolver) => (files) => { }; const getKnipCommand = (dir) => { - const posixRelative = path.posix.relative(process.cwd(), dir); - return `knip --dependencies --workspace ${posixRelative}`; + const relative = path.relative(process.cwd(), dir).split(path.sep).join('/'); + return `knip --dependencies --workspace ${relative}`; }; const runKnipConditional = (files) => { diff --git a/apps/rosen-service2/src/services/assetAggregator.ts b/apps/rosen-service2/src/services/assetAggregator.ts index 40600fa79..e334b525c 100644 --- a/apps/rosen-service2/src/services/assetAggregator.ts +++ b/apps/rosen-service2/src/services/assetAggregator.ts @@ -95,7 +95,8 @@ export class AssetAggregatorService extends PeriodicTaskService { const assetBalances: Partial> = {}; await Promise.all( Object.keys(configs.chains).map(async (chain) => { - const chainConfig = configs.chains[chain as ChainChoices]; + const chainConfig = + configs.chains[chain as keyof typeof configs.chains]; if ( chain == NETWORKS.ergo.key || ('active' in chainConfig && chainConfig.active) diff --git a/apps/rosen-service2/src/services/assetDataAdapters.ts b/apps/rosen-service2/src/services/assetDataAdapters.ts index 23a0c488d..4fb03564b 100644 --- a/apps/rosen-service2/src/services/assetDataAdapters.ts +++ b/apps/rosen-service2/src/services/assetDataAdapters.ts @@ -22,7 +22,7 @@ import { createClient } from '@vercel/kv'; import { configs } from '../configs'; import { TOTAL_SUPPLY_REDIS_KEY } from '../constants'; import { TokensConfig } from '../tokensConfig'; -import { ChainChoices, TotalSupply } from '../types'; +import { TotalSupply } from '../types'; import { stringSerializer } from '../utils'; import { DBService } from './db'; @@ -82,7 +82,9 @@ export class AssetDataAdapterService extends PeriodicTaskService { * @example * const adapter = createDataAdapter(NETWORKS.bitcoin.key, { url: "https://blockstream.info" }); */ - protected createChainSpecificDataAdapter = (chain: ChainChoices) => { + protected createChainSpecificDataAdapter = ( + chain: keyof typeof configs.chains, + ) => { const tokenMap = TokensConfig.getInstance().getTokenMap(); const addresses: string[] = [ diff --git a/packages/asset-calculator/lib/asset-calculator.ts b/packages/asset-calculator/lib/asset-calculator.ts index 2ce8f0414..babf50736 100644 --- a/packages/asset-calculator/lib/asset-calculator.ts +++ b/packages/asset-calculator/lib/asset-calculator.ts @@ -18,6 +18,7 @@ import { CardanoCalculator } from './calculator/chains/cardano-calculator'; import { DogeCalculator } from './calculator/chains/doge-calculator'; import { ErgoCalculator } from './calculator/chains/ergo-calculator'; import { EvmCalculator } from './calculator/chains/evm-calculator'; +import { FiroCalculator } from './calculator/chains/firo-calculator'; import { BridgedAssetModel } from './database/bridgedAsset/BridgedAssetModel'; import { LockedAssetEntity } from './database/lockedAsset/LockedAssetEntity'; import { LockedAssetModel } from './database/lockedAsset/LockedAssetModel'; @@ -29,6 +30,7 @@ import { DogeCalculatorInterface, ErgoCalculatorInterface, EvmCalculatorInterface, + FiroCalculatorInterface, } from './interfaces'; class AssetCalculator { @@ -49,6 +51,7 @@ class AssetCalculator { ethereumCalculator: EvmCalculatorInterface, binanceCalculator: EvmCalculatorInterface, dogeCalculator: DogeCalculatorInterface, + firoCalculator: FiroCalculatorInterface, dataSource: DataSource, protected readonly logger: AbstractLogger = new DummyLogger(), ) { @@ -101,6 +104,12 @@ class AssetCalculator { dogeCalculator.blockcypherUrl, logger.child('dogeCalculator'), ); + const firoAssetCalculator = new FiroCalculator( + this.tokens, + firoCalculator.addresses, + firoCalculator.explorerUrl, + logger.child('firoCalculator'), + ); this.calculatorMap.set(NETWORKS.ergo.key, ergoAssetCalculator); this.calculatorMap.set(NETWORKS.cardano.key, cardanoAssetCalculator); this.calculatorMap.set(NETWORKS.bitcoin.key, bitcoinAssetCalculator); @@ -111,6 +120,7 @@ class AssetCalculator { this.calculatorMap.set(NETWORKS.ethereum.key, ethereumAssetCalculator); this.calculatorMap.set(NETWORKS.binance.key, binanceAssetCalculator); this.calculatorMap.set(NETWORKS.doge.key, dogeAssetCalculator); + this.calculatorMap.set(NETWORKS.firo.key, firoAssetCalculator); this.bridgedAssetModel = new BridgedAssetModel( dataSource, logger.child('bridgedAssetModel'), diff --git a/packages/asset-calculator/lib/calculator/chains/firo-calculator.ts b/packages/asset-calculator/lib/calculator/chains/firo-calculator.ts new file mode 100644 index 000000000..7ae5afb62 --- /dev/null +++ b/packages/asset-calculator/lib/calculator/chains/firo-calculator.ts @@ -0,0 +1,63 @@ +import { AbstractLogger } from '@rosen-bridge/abstract-logger'; +import { NATIVE_TOKEN, RosenChainToken, TokenMap } from '@rosen-bridge/tokens'; +import axios, { Axios } from '@rosen-clients/rate-limited-axios'; +import { NETWORKS } from '@rosen-ui/constants'; +import { Network } from '@rosen-ui/types'; +import { zipWith } from 'lodash-es'; + +import AbstractCalculator from '../abstract-calculator'; + +export class FiroCalculator extends AbstractCalculator { + readonly chain: Network = NETWORKS.firo.key; + + protected client: Axios; + + constructor( + tokenMap: TokenMap, + addresses: string[], + url: string = 'https://explorer.firo.org', + logger?: AbstractLogger, + ) { + super(addresses, logger, tokenMap); + this.client = axios.create({ + baseURL: url, + }); + } + + /** + * @param token Firo chain token supply, always 0 + */ + totalRawSupply = async (): Promise => { + return 0n; + }; + + /** + * @param token Firo chain token balance, always 0 + */ + totalRawBalance = async (): Promise => { + return 0n; + }; + + /** + * returns locked amounts of a specific token for different addresses + * @param token + */ + getRawLockedAmountsPerAddress = async (token: RosenChainToken) => { + if (token.type === NATIVE_TOKEN) { + const balances = await Promise.all( + this.addresses.map(async (address) => { + const response = await this.client.get( + `/insight-api-zcoin/addr/${address}/balance`, + ); + return BigInt(response.data); + }), + ); + return zipWith(this.addresses, balances, (address, amount) => ({ + address, + amount, + })).filter((amountPerAddress) => amountPerAddress.amount); + } + + return []; + }; +} diff --git a/packages/asset-calculator/lib/interfaces.ts b/packages/asset-calculator/lib/interfaces.ts index 03ab3023c..7f1427aa1 100644 --- a/packages/asset-calculator/lib/interfaces.ts +++ b/packages/asset-calculator/lib/interfaces.ts @@ -18,6 +18,10 @@ interface DogeCalculatorInterface extends CalculatorInterface { blockcypherUrl: string; } +interface FiroCalculatorInterface extends CalculatorInterface { + explorerUrl?: string; +} + interface EvmCalculatorInterface extends CalculatorInterface { rpcUrl: string; authToken?: string; @@ -34,4 +38,5 @@ export { BitcoinRunesCalculatorInterface, EvmCalculatorInterface, DogeCalculatorInterface, + FiroCalculatorInterface, }; diff --git a/packages/asset-calculator/tests/asset-calculator.spec.ts b/packages/asset-calculator/tests/asset-calculator.spec.ts index 7c6d1d068..ad8866256 100644 --- a/packages/asset-calculator/tests/asset-calculator.spec.ts +++ b/packages/asset-calculator/tests/asset-calculator.spec.ts @@ -33,6 +33,10 @@ describe('AssetCalculator', () => { addresses: ['hotAddr', 'coldAddr'], blockcypherUrl: 'blockcypherUrl', }, + { + addresses: ['hotAddr', 'coldAddr'], + explorerUrl: 'firoExplorerUrl', + }, dataSource, ); }); @@ -120,6 +124,10 @@ describe('AssetCalculator', () => { addresses: ['hotAddr', 'coldAddr'], blockcypherUrl: 'blockcypherUrl', }, + { + addresses: ['hotAddr', 'coldAddr'], + explorerUrl: 'firoExplorerUrl', + }, dataSource, ); }); @@ -193,6 +201,7 @@ describe('AssetCalculator', () => { { addresses: ['Addr'], rpcUrl: 'rpcUrl' }, { addresses: ['Addr'], rpcUrl: 'bnbRpcUrl' }, { addresses: ['Addr'], blockcypherUrl: 'blockcypherUrl' }, + { addresses: ['Addr'], explorerUrl: 'firoExplorerUrl' }, dataSource, ); assetCalculator['totalSupplyInit'] = true; @@ -312,6 +321,7 @@ describe('AssetCalculator', () => { { addresses: ['Addr'], rpcUrl: 'rpcUrl' }, { addresses: ['Addr'], rpcUrl: 'bnbRpcUrl' }, { addresses: ['Addr'], blockcypherUrl: 'blockcypherUrl' }, + { addresses: ['Addr'], explorerUrl: 'firoExplorerUrl' }, dataSource, ); assetCalculator['totalSupplyInit'] = true; @@ -422,6 +432,10 @@ describe('AssetCalculator', () => { addresses: ['hotAddr', 'coldAddr'], blockcypherUrl: 'blockcypherUrl', }, + { + addresses: ['hotAddr', 'coldAddr'], + explorerUrl: 'firoExplorerUrl', + }, dataSource, ); }); diff --git a/packages/constants/src/index.ts b/packages/constants/src/index.ts index cfc149ac5..cff2f29b3 100644 --- a/packages/constants/src/index.ts +++ b/packages/constants/src/index.ts @@ -65,6 +65,14 @@ export const NETWORKS = { id: '', hasTokenSupport: false, }, + 'firo': { + index: 7, + key: 'firo', + label: 'Firo', + nativeToken: 'firo', + id: '', + hasTokenSupport: false, + }, } as const; export const NETWORKS_KEYS = Object.values(NETWORKS).map(