diff --git a/.c8rc b/.c8rc new file mode 100644 index 0000000..366fa1d --- /dev/null +++ b/.c8rc @@ -0,0 +1,11 @@ +{ + "all": true, + "include": ["src/**/*.ts"], + "exclude": ["**/*.test.ts", "src/tests/**", "src/types/**", "src/**/*.d.ts", "src/**/types.ts"], + "reporter": ["text", "text-summary", "html", "lcov"], + "check-coverage": true, + "lines": 80, + "functions": 80, + "branches": 80, + "statements": 80 +} diff --git a/.gitignore b/.gitignore index 98ece02..bfb85d9 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ yarn-error.log !.yarn/versions .env +coverage/ diff --git a/.mocharc b/.mocharc new file mode 100644 index 0000000..ebdeeb3 --- /dev/null +++ b/.mocharc @@ -0,0 +1,10 @@ +{ + "extension": [ + "ts" + ], + "node-option": [ + "experimental-specifier-resolution=node", + "loader=ts-node/esm" + ], + "spec": "src/**/*.test.ts" +} diff --git a/README.md b/README.md index 6e2baff..beeedfd 100644 --- a/README.md +++ b/README.md @@ -81,3 +81,32 @@ const subscription = pool.closeEpoch().subscribe( () => console.log('complete') ) ``` + +## Reports + +Reports are generated from the subquery pool indexer and are combined with pool metadata to provide a comprehensive view of the pool's financials. + +Available reports are: + +- `profitAndLoss` +- `balanceSheet` +- `profitAndLoss` + +```ts +const pool = await centrifuge.pool('') +const balanceSheetReport = await pool.reports.balanceSheet() +``` + +### Report Filtering + +Reports can be filtered using the `ReportFilter` type. + +```ts +type GroupBy = 'day' | 'month' | 'quarter' | 'year' + +const balanceSheetReport = await pool.reports.balanceSheet({ + from: '2024-01-01', + to: '2024-01-31', + groupBy: 'month', +}) +``` diff --git a/package.json b/package.json index 4d2d2bf..2d88d71 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,10 @@ "dev": "tsc -w --importHelpers", "build": "tsc --importHelpers", "prepare": "yarn build", - "test": "mocha --loader=ts-node/esm --require $(pwd)/src/tests/setup.ts --exit --timeout 60000 'src/**/*.test.ts'", + "test": "c8 mocha --loader=ts-node/esm --require $(pwd)/src/tests/setup.ts --exit --timeout 60000 'src/**/*.test.ts' --recursive", "test:simple:single": "mocha --loader=ts-node/esm --exit --timeout 60000", - "test:single": "mocha --loader=ts-node/esm --require $(pwd)/src/tests/setup.ts --exit --timeout 60000" + "test:single": "mocha --loader=ts-node/esm --require $(pwd)/src/tests/setup.ts --exit --timeout 60000", + "coverage": "c8 report --reporter=text --reporter=lcov" }, "dependencies": { "decimal.js-light": "^2.5.1", @@ -41,6 +42,7 @@ "@types/node": "^22.7.8", "@types/sinon": "^17.0.3", "@types/sinon-chai": "^4", + "c8": "^10.1.2", "chai": "^5.1.2", "dotenv": "^16.4.5", "eslint": "^9.12.0", @@ -50,6 +52,7 @@ "prettier": "^3.3.3", "sinon": "^19.0.2", "sinon-chai": "^4.0.0", + "source-map-support": "^0.5.21", "ts-node": "^10.9.2", "typescript": "~5.6.3", "typescript-eslint": "^8.8.1", diff --git a/src/Centrifuge.ts b/src/Centrifuge.ts index 21016d2..b868566 100644 --- a/src/Centrifuge.ts +++ b/src/Centrifuge.ts @@ -41,6 +41,7 @@ export type Config = { environment: 'mainnet' | 'demo' | 'dev' rpcUrls?: Record indexerUrl: string + ipfsUrl: string } export type UserProvidedConfig = Partial @@ -49,6 +50,7 @@ type EnvConfig = { alchemyKey: string infuraKey: string defaultChain: number + ipfsUrl: string } type DerivedConfig = Config & EnvConfig @@ -58,18 +60,21 @@ const envConfig = { alchemyKey: 'KNR-1LZhNqWOxZS2AN8AFeaiESBV10qZ', infuraKey: '8ed99a9a115349bbbc01dcf3a24edc96', defaultChain: 1, + ipfsUrl: 'https://centrifuge.mypinata.cloud', }, demo: { indexerUrl: 'https://api.subquery.network/sq/centrifuge/pools-demo-multichain', alchemyKey: 'KNR-1LZhNqWOxZS2AN8AFeaiESBV10qZ', infuraKey: '8cd8e043ee8d4001b97a1c37e08fd9dd', defaultChain: 11155111, + ipfsUrl: 'https://centrifuge.mypinata.cloud', }, dev: { indexerUrl: 'https://api.subquery.network/sq/centrifuge/pools-demo-multichain', alchemyKey: 'KNR-1LZhNqWOxZS2AN8AFeaiESBV10qZ', infuraKey: '8cd8e043ee8d4001b97a1c37e08fd9dd', defaultChain: 11155111, + ipfsUrl: 'https://centrifuge.mypinata.cloud', }, } satisfies Record @@ -124,8 +129,8 @@ export class Centrifuge { }) } - pool(id: string) { - return this._query(null, () => of(new Pool(this, id))) + pool(id: string, metadataHash?: string) { + return this._query(null, () => of(new Pool(this, id, metadataHash))) } account(address: string, chainId?: number) { @@ -224,6 +229,31 @@ export class Centrifuge { ) } + /** + * @internal + */ + _getIPFSObservable(hash: string) { + return fromFetch(`${this.config.ipfsUrl}/ipfs/${hash}`, { + method: 'GET', + selector: async (res) => { + if (!res.ok) { + console.warn(`Failed to fetch IPFS data: ${res.statusText}`) + } + const data = await res.json() + return data as T + }, + }) + } + + /** + * @internal + */ + _queryIPFS(hash: string): Query { + return this._query([hash], () => this._getIPFSObservable(hash), { + valueCacheTime: 120, + }) + } + #memoized = new Map() #memoizeWith(keys: any[], callback: () => T): T { const cacheKey = hashKey(serializeForCache(keys)) diff --git a/src/Entity.ts b/src/Entity.ts index bfeaf82..ad3694b 100644 --- a/src/Entity.ts +++ b/src/Entity.ts @@ -16,10 +16,10 @@ export class Entity { } protected _query( - keys: (string | number)[] | null, + keys: (string | number | undefined)[] | null, observableCallback: () => Observable, options?: CentrifugeQueryOptions ) { - return this._root._query(keys ? [...this.#baseKeys, ...keys] : null, observableCallback, options) + return this._root._query(keys ? [...this.#baseKeys, ...keys.filter(Boolean)] : null, observableCallback, options) } } diff --git a/src/Pool.ts b/src/Pool.ts index e63d473..43160c0 100644 --- a/src/Pool.ts +++ b/src/Pool.ts @@ -3,17 +3,23 @@ import type { Centrifuge } from './Centrifuge.js' import { Entity } from './Entity.js' import { Reports } from './Reports/index.js' import { PoolNetwork } from './PoolNetwork.js' +import { PoolMetadata } from './types/poolMetadata.js' export class Pool extends Entity { constructor( _root: Centrifuge, - public id: string + public id: string, + public metadataHash?: string ) { super(_root, ['pool', id]) } get reports() { - return new Reports(this._root, this.id) + return new Reports(this._root, this.id, this.metadataHash) + } + + metadata() { + return this.metadataHash ? this._root._queryIPFS(this.metadataHash) : of(null) } trancheIds() { diff --git a/src/Reports/Processor.test.ts b/src/Reports/Processor.test.ts new file mode 100644 index 0000000..edc2faa --- /dev/null +++ b/src/Reports/Processor.test.ts @@ -0,0 +1,442 @@ +import { expect } from 'chai' +import { processor } from './Processor.js' +import { mockPoolSnapshots } from '../tests/mocks/mockPoolSnapshots.js' +import { mockTrancheSnapshots } from '../tests/mocks/mockTrancheSnapshots.js' +import { mockPoolFeeSnapshots } from '../tests/mocks/mockPoolFeeSnapshot.js' +import { mockPoolMetadata } from '../tests/mocks/mockPoolMetadata.js' +import { PoolSnapshot } from '../queries/poolSnapshots.js' +import { Currency } from '../utils/BigInt.js' +import { PoolFeeSnapshot, PoolFeeSnapshotsByDate } from '../queries/poolFeeSnapshots.js' +import { ProfitAndLossReportPrivateCredit, ProfitAndLossReportPublicCredit } from './types.js' + +describe('Processor', () => { + describe('balanceSheet processor', () => { + it('should return empty array when no pool snapshots found', () => { + expect( + processor.balanceSheet({ + poolSnapshots: [], + trancheSnapshots: {}, + }) + ).to.deep.equal([]) + }) + it('should throw error when no tranches found', () => { + expect(() => + processor.balanceSheet({ + poolSnapshots: mockPoolSnapshots, + trancheSnapshots: {}, + }) + ).to.throw('No tranches found for snapshot') + }) + it('should process pool and tranche data correctly', () => { + const result = processor.balanceSheet({ + poolSnapshots: mockPoolSnapshots, + trancheSnapshots: mockTrancheSnapshots, + }) + + expect(result).to.have.lengthOf(2) + const report = result[0] + + expect(report?.timestamp).to.equal('2024-01-01T12:00:00Z') + expect(report?.assetValuation.toBigInt()).to.equal(0n) + expect(report?.onchainReserve.toBigInt()).to.equal(0n) + expect(report?.offchainCash.toBigInt()).to.equal(0n) + expect(report?.accruedFees.toBigInt()).to.equal(0n) + expect(report?.netAssetValue.toBigInt()).to.equal(0n) + + expect(report?.tranches?.length).to.equal(2) + const seniorTranche = report?.tranches?.find((t) => t.tokenId === 'senior')! + const juniorTranche = report?.tranches?.find((t) => t.tokenId === 'junior')! + + expect(seniorTranche?.tokenPrice!.toBigInt()).to.equal(1000000000000000000n) + expect(juniorTranche?.tokenPrice!.toBigInt()).to.equal(1120000000000000000n) + expect(report?.totalCapital?.toBigInt()).to.equal(0n) + }) + it('should group data by day when specified', () => { + const result = processor.balanceSheet( + { + poolSnapshots: mockPoolSnapshots, + trancheSnapshots: mockTrancheSnapshots, + }, + { groupBy: 'day' } + ) + + expect(result).to.have.lengthOf(2) + expect(result?.[0]?.timestamp.slice(0, 10)).to.equal('2024-01-01') + expect(result?.[1]?.timestamp.slice(0, 10)).to.equal('2024-01-02') + }) + + it('should group data by month when specified', () => { + const result = processor.balanceSheet( + { + poolSnapshots: mockPoolSnapshots, + trancheSnapshots: mockTrancheSnapshots, + }, + { groupBy: 'month' } + ) + + expect(result).to.have.lengthOf(1) + expect(result?.[0]?.timestamp.slice(0, 10)).to.equal('2024-01-02') + }) + }) + + describe('cashflow processor', () => { + const mockCashflowPoolSnapshots: PoolSnapshot[] = [ + { + ...mockPoolSnapshots[0], + id: 'pool-10', + timestamp: '2024-01-01T12:00:00Z', + sumPrincipalRepaidAmountByPeriod: Currency.fromFloat(1, 6), // 1.0 + sumInterestRepaidAmountByPeriod: Currency.fromFloat(0.05, 6), // 0.05 + sumBorrowedAmountByPeriod: Currency.fromFloat(2, 6), // 2.0 + }, + { + ...mockPoolSnapshots[0], + id: 'pool-11', + timestamp: '2024-01-01T18:00:00Z', // Same day, different time + sumPrincipalRepaidAmountByPeriod: Currency.fromFloat(0.5, 6), // 0.5 + sumInterestRepaidAmountByPeriod: Currency.fromFloat(0.025, 6), // 0.025 + sumBorrowedAmountByPeriod: Currency.fromFloat(1, 6), // 1.0 + }, + { + ...mockPoolSnapshots[0], + id: 'pool-12', + timestamp: '2024-01-02T12:00:00Z', // Next day + sumPrincipalRepaidAmountByPeriod: Currency.fromFloat(2, 6), // 2.0 + sumInterestRepaidAmountByPeriod: Currency.fromFloat(0.1, 6), // 0.1 + sumBorrowedAmountByPeriod: Currency.fromFloat(4, 6), // 4.0 + }, + ] as PoolSnapshot[] + + const mockCashflowFeeSnapshots: PoolFeeSnapshotsByDate = { + '2024-01-01': [ + { + ...mockPoolFeeSnapshots['2024-01-01']?.[0], + poolFee: { name: 'serviceFee' }, + sumPaidAmountByPeriod: new Currency(1_000_000n, 6), // 1.0 + sumAccruedAmountByPeriod: new Currency(500_000n, 6), // 0.5 + } as PoolFeeSnapshot, + { + ...mockPoolFeeSnapshots['2024-01-01']?.[1], + poolFee: { name: 'adminFee' }, + sumPaidAmountByPeriod: new Currency(2_000_000n, 6), // 2.0 + sumAccruedAmountByPeriod: new Currency(1_000_000n, 6), // 1.0 + } as PoolFeeSnapshot, + ], + '2024-01-02': [ + { + ...mockPoolFeeSnapshots['2024-01-02']?.[0], + poolFee: { name: 'serviceFee' }, + sumPaidAmountByPeriod: new Currency(3_000_000n, 6), // 3.0 + sumAccruedAmountByPeriod: new Currency(1_500_000n, 6), // 1.5 + } as PoolFeeSnapshot, + { + ...mockPoolFeeSnapshots['2024-01-02']?.[1], + poolFee: { name: 'adminFee' }, + sumPaidAmountByPeriod: new Currency(4_000_000n, 6), // 4.0 + sumAccruedAmountByPeriod: new Currency(2_000_000n, 6), // 2.0 + } as PoolFeeSnapshot, + ], + } + + it('should return empty array when no snapshots found', () => { + expect(processor.cashflow({ poolSnapshots: [], poolFeeSnapshots: {}, metadata: undefined })).to.deep.equal([]) + }) + + it('should aggregate values correctly when grouping by day', () => { + const result = processor.cashflow( + { + poolSnapshots: mockCashflowPoolSnapshots, + poolFeeSnapshots: mockCashflowFeeSnapshots, + metadata: mockPoolMetadata, + }, + { groupBy: 'day' } + ) + + expect(result).to.have.lengthOf(2) + + const jan1 = result[0] + expect(jan1?.timestamp.slice(0, 10)).to.equal('2024-01-01') + expect(jan1?.principalPayments.toFloat()).to.equal(1.5) // 1.0 + 0.5 + expect(jan1?.interestPayments.toFloat()).to.equal(0.075) // 0.05 + 0.025 + expect(jan1?.assetAcquisitions.toFloat()).to.equal(3) // 2.0 + 1.0 + expect(jan1?.fees.length).to.equal(2) + expect(jan1?.fees[0]?.name).to.equal('serviceFee') + expect(jan1?.fees[1]?.name).to.equal('adminFee') + expect(jan1?.fees[0]?.amount.toFloat()).to.equal(1) + expect(jan1?.fees[1]?.amount.toFloat()).to.equal(2) + expect(jan1?.fees[0]?.timestamp.slice(0, 10)).to.equal('2024-01-01') + expect(jan1?.fees[1]?.timestamp.slice(0, 10)).to.equal('2024-01-01') + + const jan2 = result[1] + expect(jan2?.timestamp.slice(0, 10)).to.equal('2024-01-02') + expect(jan2?.principalPayments.toFloat()).to.equal(2) // 2.0 + expect(jan2?.interestPayments.toFloat()).to.equal(0.1) // 0.1 + expect(jan2?.assetAcquisitions.toFloat()).to.equal(4) // 4.0 + expect(jan2?.fees.length).to.equal(2) + expect(jan2?.fees[0]?.name).to.equal('serviceFee') + expect(jan2?.fees[1]?.name).to.equal('adminFee') + expect(jan2?.fees[0]?.amount.toFloat()).to.equal(3) + expect(jan2?.fees[1]?.amount.toFloat()).to.equal(4) + expect(jan2?.fees[0]?.timestamp.slice(0, 10)).to.equal('2024-01-02') + expect(jan2?.fees[1]?.timestamp.slice(0, 10)).to.equal('2024-01-02') + }) + + it('should aggregate values correctly when grouping by month', () => { + const result = processor.cashflow( + { + poolSnapshots: mockCashflowPoolSnapshots, + poolFeeSnapshots: mockCashflowFeeSnapshots, + metadata: mockPoolMetadata, + }, + { groupBy: 'month' } + ) + + expect(result).to.have.lengthOf(1) + + const january = result[0] + expect(january?.timestamp.slice(0, 10)).to.equal('2024-01-02') // Grouping by month returns the last day of the period + expect(january?.principalPayments.toFloat()).to.equal(3.5) // 1.0 + 0.5 + 2.0 + expect(january?.interestPayments.toFloat()).to.equal(0.175) // 0.05 + 0.025 + 0.1 + expect(january?.assetAcquisitions.toFloat()).to.equal(7) // 2.0 + 1.0 + 4.0 + expect(january?.fees.length).to.equal(2) + expect(january?.fees[0]?.name).to.equal('serviceFee') + expect(january?.fees[1]?.name).to.equal('adminFee') + // fees are NOT aggregated by period + expect(january?.fees[0]?.amount.toFloat()).to.equal(3) + expect(january?.fees[1]?.amount.toFloat()).to.equal(4) + }) + it('should return realizedPL if pool is public credit', () => { + const result = processor.cashflow( + { + poolSnapshots: mockCashflowPoolSnapshots, + poolFeeSnapshots: mockCashflowFeeSnapshots, + metadata: { + ...mockPoolMetadata, + pool: { ...mockPoolMetadata.pool, asset: { ...mockPoolMetadata.pool.asset, class: 'Public credit' } }, + }, + }, + { groupBy: 'day' } + ) + expect(result?.[0]).to.have.property('realizedPL') + }) + }) + describe('profit and loss processor', () => { + const mockPLPoolSnapshots: PoolSnapshot[] = [ + { + ...mockPoolSnapshots[0], + id: 'pool-10', + timestamp: '2024-01-01T12:00:00Z', + sumInterestRepaidAmountByPeriod: Currency.fromFloat(0.05, 6), // 0.05 + sumInterestAccruedByPeriod: Currency.fromFloat(0.1, 6), // 0.1 + sumDebtWrittenOffByPeriod: Currency.fromFloat(0.02, 6), // 0.02 + sumUnscheduledRepaidAmountByPeriod: Currency.fromFloat(0.01, 6), // 0.01 + sumUnrealizedProfitByPeriod: Currency.fromFloat(0.15, 6), // 0.15 + }, + { + ...mockPoolSnapshots[0], + id: 'pool-11', + timestamp: '2024-01-02T12:00:00Z', + sumInterestRepaidAmountByPeriod: Currency.fromFloat(0.1, 6), + sumInterestAccruedByPeriod: Currency.fromFloat(0.2, 6), + sumDebtWrittenOffByPeriod: Currency.fromFloat(0.03, 6), + sumUnscheduledRepaidAmountByPeriod: Currency.fromFloat(0.02, 6), + sumUnrealizedProfitByPeriod: Currency.fromFloat(0.25, 6), + }, + ] as PoolSnapshot[] + + const mockPLFeeSnapshots: PoolFeeSnapshotsByDate = { + '2024-01-01': [ + { + poolFee: { name: 'serviceFee' }, + sumAccruedAmountByPeriod: Currency.fromFloat(0.01, 6), + sumChargedAmountByPeriod: Currency.fromFloat(0.02, 6), + timestamp: '2024-01-01T12:00:00Z', + poolFeeId: 'pool-fee-1', + } as PoolFeeSnapshot, + ], + '2024-01-02': [ + { + poolFee: { name: 'serviceFee' }, + sumAccruedAmountByPeriod: Currency.fromFloat(0.02, 6), + sumChargedAmountByPeriod: Currency.fromFloat(0.03, 6), + timestamp: '2024-01-02T12:00:00Z', + poolFeeId: 'pool-fee-2', + } as PoolFeeSnapshot, + ], + } + + it('should return empty array when no snapshots found', () => { + expect(processor.profitAndLoss({ poolSnapshots: [], poolFeeSnapshots: {}, metadata: undefined })).to.deep.equal( + [] + ) + }) + + it('should process private credit pool correctly', () => { + const result = processor.profitAndLoss({ + poolSnapshots: mockPLPoolSnapshots, + poolFeeSnapshots: mockPLFeeSnapshots, + metadata: mockPoolMetadata, // defaults to private credit + }) + + expect(result).to.have.lengthOf(2) + const firstDay = result[0] as ProfitAndLossReportPrivateCredit + + expect(firstDay?.subtype).to.equal('privateCredit') + expect(firstDay).to.have.property('interestAccrued') + expect(firstDay).to.have.property('assetWriteOffs') + expect(firstDay?.interestAccrued.toFloat()).to.equal(0.1) + expect(firstDay?.assetWriteOffs.toFloat()).to.equal(0.02) + expect(firstDay?.interestPayments.toFloat()).to.equal(0.05) + expect(firstDay?.otherPayments.toFloat()).to.equal(0.01) + expect(firstDay?.profitAndLossFromAsset.toFloat()).to.equal(0.16) // 0.05 + 0.1 + 0.02 - 0.01 + }) + + it('should process public credit pool correctly', () => { + const result = processor.profitAndLoss({ + poolSnapshots: mockPLPoolSnapshots, + poolFeeSnapshots: mockPLFeeSnapshots, + metadata: { + ...mockPoolMetadata, + pool: { + ...mockPoolMetadata.pool, + asset: { ...mockPoolMetadata.pool.asset, class: 'Public credit' }, + }, + }, + }) + + expect(result).to.have.lengthOf(2) + const firstDay = result[0] as ProfitAndLossReportPublicCredit + + expect(firstDay?.subtype).to.equal('publicCredit') + expect(firstDay?.profitAndLossFromAsset.toFloat()).to.equal(0.15) // unrealized profit + expect(firstDay?.interestPayments.toFloat()).to.equal(0.05) + expect(firstDay?.otherPayments.toFloat()).to.equal(0.01) + expect(firstDay).to.have.property('totalIncome') + expect(firstDay?.totalIncome.toFloat()).to.equal(0.21) // 0.15 + 0.05 + 0.01 + }) + + it('should aggregate values correctly when grouping by month', () => { + const result = processor.profitAndLoss( + { + poolSnapshots: mockPLPoolSnapshots, + poolFeeSnapshots: mockPLFeeSnapshots, + metadata: mockPoolMetadata, + }, + { groupBy: 'month' } + ) + + expect(result).to.have.lengthOf(1) + const january = result[0] + expect(january?.timestamp.slice(0, 10)).to.equal('2024-01-02') + expect(january?.interestPayments.toFloat()).to.equal(0.15) // 0.05 + 0.1 + expect(january?.otherPayments.toFloat()).to.equal(0.03) // 0.01 + 0.02 + }) + it('should make sure timestamp for pool state and pool fee match', () => { + const result = processor.profitAndLoss({ + poolSnapshots: mockPLPoolSnapshots, + poolFeeSnapshots: mockPLFeeSnapshots, + metadata: mockPoolMetadata, + }) + expect(result[0]?.timestamp.slice(0, 10)).to.equal('2024-01-01') + expect(result[0]?.fees?.[0]?.timestamp.slice(0, 10)).to.equal('2024-01-01') + }) + }) + describe('applyGrouping', () => { + const applyGrouping = processor['applyGrouping'] + const mockData = [ + { a: Currency.fromFloat(10, 6), timestamp: '2024-01-01' }, + { a: Currency.fromFloat(20, 6), timestamp: '2024-01-01' }, + ] + it('should return empty array when no items found', () => { + expect(applyGrouping([], 'day', 'sum')).to.deep.equal([]) + }) + it('should return items by day when no grouping is specified', () => { + const latest = applyGrouping(mockData, undefined, 'latest') + expect(latest).to.deep.equal([{ a: Currency.fromFloat(20, 6), timestamp: '2024-01-01' }]) + const summed = applyGrouping(mockData, undefined, 'sum') + expect(summed).to.deep.equal([{ a: Currency.fromFloat(30, 6), timestamp: '2024-01-01' }]) + }) + it('should return latest item when strategy is latest', () => { + const grouped = applyGrouping(mockData, 'day', 'latest') + expect(grouped).to.deep.equal([{ a: Currency.fromFloat(20, 6), timestamp: '2024-01-01' }]) + }) + it('should aggregate values when strategy is sum', () => { + const grouped = applyGrouping(mockData, 'day', 'sum') + expect(grouped).to.deep.equal([{ a: Currency.fromFloat(30, 6), timestamp: '2024-01-01' }]) + }) + it('should return latest item when strategy is latest and no grouping is specified', () => { + const grouped = applyGrouping(mockData, undefined, 'latest') + expect(grouped).to.deep.equal([{ a: Currency.fromFloat(20, 6), timestamp: '2024-01-01' }]) + }) + it('should aggregate values when strategy is sum and no grouping is specified', () => { + const grouped = applyGrouping(mockData, undefined, 'sum') + expect(grouped).to.deep.equal([{ a: Currency.fromFloat(30, 6), timestamp: '2024-01-01' }]) + }) + it('should return latest item when strategy is latest and grouping is month', () => { + const extendedMockData = [...mockData, { a: Currency.fromFloat(30, 6), timestamp: '2024-02-01' }] + const grouped = applyGrouping(extendedMockData, 'month', 'latest') + const expected = [ + { a: Currency.fromFloat(20, 6), timestamp: '2024-01-01' }, + { a: Currency.fromFloat(30, 6), timestamp: '2024-02-01' }, + ] + expect(grouped).to.deep.equal(expected) + }) + it('should aggregate values when strategy is sum and grouping is month', () => { + const extendedMockData = [...mockData, { a: Currency.fromFloat(30, 6), timestamp: '2024-02-01' }] + const grouped = applyGrouping(extendedMockData, 'month', 'sum') + const expected = [ + { a: Currency.fromFloat(30, 6), timestamp: '2024-01-01' }, + { a: Currency.fromFloat(30, 6), timestamp: '2024-02-01' }, + ] + expect(grouped).to.deep.equal(expected) + }) + it('should only aggregate top-level Currency values and use last value for nested objects', () => { + const items = [ + { + timestamp: '2024-01-01T12:00:00Z', + topLevelAmount: Currency.fromFloat(1, 6), // should be summed + nested: { + amount: Currency.fromFloat(1, 6), // should take last value + description: 'first', + }, + fees: [ + { + amount: Currency.fromFloat(0.5, 6), // should take last value + name: 'fee1', + }, + ], + }, + { + timestamp: '2024-01-01T18:00:00Z', + topLevelAmount: Currency.fromFloat(2, 6), + nested: { + amount: Currency.fromFloat(3, 6), + description: 'second', + }, + fees: [ + { + amount: Currency.fromFloat(0.7, 6), + name: 'fee1', + }, + ], + }, + ] + + const result = processor['applyGrouping'](items, 'day', 'sum') + + expect(result).to.have.lengthOf(1) + const aggregated = result[0] + + // Top level Currency should be summed + expect(aggregated?.topLevelAmount.toFloat()).to.equal(3) // 1 + 2 + + // Nested Currency should be from last item + expect(aggregated?.nested?.amount.toFloat()).to.equal(3) // last value only + expect(aggregated?.nested?.description).to.equal('second') + + // Array of objects with Currency should be from last item + expect(aggregated?.fees[0]?.amount.toFloat()).to.equal(0.7) + expect(aggregated?.fees[0]?.name).to.equal('fee1') + }) + }) +}) diff --git a/src/Reports/Processor.ts b/src/Reports/Processor.ts new file mode 100644 index 0000000..b39ca48 --- /dev/null +++ b/src/Reports/Processor.ts @@ -0,0 +1,196 @@ +import { Currency } from '../utils/BigInt.js' +import { groupByPeriod } from '../utils/date.js' +import { + BalanceSheetData, + BalanceSheetReport, + CashflowData, + CashflowReport, + ProfitAndLossReport, + ProfitAndLossData, + ReportFilter, +} from './types.js' + +export class Processor { + /** + * Process raw data into a balance sheet report + * @param data Pool and tranche snapshot data + * @param filter Optional filtering and grouping options + * @returns Processed balance sheet report at the end of each period + */ + balanceSheet(data: BalanceSheetData, filter?: ReportFilter): BalanceSheetReport[] { + const items: BalanceSheetReport[] = data?.poolSnapshots?.map((snapshot) => { + const tranches = data.trancheSnapshots[this.getDateKey(snapshot.timestamp)] ?? [] + if (tranches.length === 0) throw new Error('No tranches found for snapshot') + return { + type: 'balanceSheet', + timestamp: snapshot.timestamp, + assetValuation: snapshot.portfolioValuation, + onchainReserve: snapshot.totalReserve, + offchainCash: snapshot.offchainCashValue, + accruedFees: snapshot.sumPoolFeesPendingAmount, + netAssetValue: snapshot.netAssetValue, + tranches: tranches?.map((tranche) => ({ + name: tranche.pool.currency.symbol, + timestamp: tranche.timestamp, + tokenId: tranche.trancheId, + tokenSupply: tranche.tokenSupply, + tokenPrice: tranche.price, + trancheValue: tranche.tokenSupply.mul(tranche?.price?.toBigInt() ?? 0n), + })), + totalCapital: tranches.reduce( + (acc, curr) => acc.add(curr.tokenSupply.mul(curr?.price?.toBigInt() ?? 0n).toBigInt()), + new Currency(0, snapshot.poolCurrency.decimals) + ), + } + }) + return this.applyGrouping(items, filter?.groupBy, 'latest') + } + + /** + * Process raw data into an aggregated cashflow report, fees and endCashBalance are NOT aggregated by period + * @param data Pool snapshot data + * @param filter Optional filtering and grouping options + * @returns Processed cashflow report at the end of each period + */ + cashflow(data: CashflowData, filter?: ReportFilter): CashflowReport[] { + const subtype = data.metadata?.pool.asset.class === 'Public credit' ? 'publicCredit' : 'privateCredit' + const items: CashflowReport[] = data.poolSnapshots.map((day) => { + const poolFees = + data.poolFeeSnapshots[this.getDateKey(day.timestamp)]?.map((fee) => ({ + name: fee.poolFee.name, + amount: fee.sumPaidAmountByPeriod, + timestamp: fee.timestamp, + feeId: fee.poolFeeId.split('-')[1] ?? '', + })) ?? [] + const principalRepayments = day.sumPrincipalRepaidAmountByPeriod + const interest = day.sumInterestRepaidAmountByPeriod.add(day.sumUnscheduledRepaidAmountByPeriod) + const aquisistions = day.sumBorrowedAmountByPeriod + const fees = day.sumPoolFeesPaidAmountByPeriod + const netCashflowAsset = principalRepayments.sub(aquisistions).add(interest) + const investments = day.sumInvestedAmountByPeriod + const redemptions = day.sumRedeemedAmountByPeriod + const activitiesCashflow = investments.sub(redemptions) + const netCashflowAfterFees = netCashflowAsset.sub(fees) + const totalCashflow = netCashflowAfterFees.add(activitiesCashflow) + return { + type: 'cashflow', + subtype, + timestamp: day.timestamp, + principalPayments: principalRepayments, + ...(subtype === 'publicCredit' && { realizedPL: day.sumRealizedProfitFifoByPeriod }), + interestPayments: interest, + assetAcquisitions: aquisistions, + netCashflowAsset, + fees: poolFees, + netCashflowAfterFees, + investments, + redemptions, + activitiesCashflow, + totalCashflow, + endCashBalance: { balance: day.totalReserve.add(day.offchainCashValue) }, + } + }) + return this.applyGrouping(items, filter?.groupBy, 'sum') + } + + /** + * Process raw data into an aggregated profit and loss report, fees and endCashBalance are NOT aggregated by period + * @param data Pool snapshot data + * @param filter Optional filtering and grouping options + * @returns Processed profit and loss report at the end of each period + */ + profitAndLoss(data: ProfitAndLossData, filter?: ReportFilter): ProfitAndLossReport[] { + const items: ProfitAndLossReport[] = data.poolSnapshots.map((day) => { + const subtype = data.metadata?.pool.asset.class === 'Public credit' ? 'publicCredit' : 'privateCredit' + const profitAndLossFromAsset = + subtype === 'publicCredit' + ? day.sumUnrealizedProfitByPeriod + : day.sumInterestRepaidAmountByPeriod + .add(day.sumInterestAccruedByPeriod) + .add(day.sumDebtWrittenOffByPeriod) + .sub(day.sumUnscheduledRepaidAmountByPeriod) + const interestPayments = day.sumInterestRepaidAmountByPeriod + const otherPayments = day.sumUnscheduledRepaidAmountByPeriod + const totalIncome = profitAndLossFromAsset.add(interestPayments).add(otherPayments) + const fees = + data.poolFeeSnapshots[this.getDateKey(day.timestamp)]?.map((fee) => ({ + name: fee.poolFee.name, + amount: fee.sumAccruedAmountByPeriod.add(fee.sumChargedAmountByPeriod), + timestamp: fee.timestamp, + feeId: fee.poolFeeId.split('-')[1] ?? '', + })) ?? [] + const totalExpenses = day.sumPoolFeesChargedAmountByPeriod.sub(day.sumPoolFeesAccruedAmountByPeriod) + const totalProfitAndLoss = totalIncome.sub(totalExpenses) + return { + type: 'profitAndLoss', + subtype, + timestamp: day.timestamp, + profitAndLossFromAsset, + interestPayments, + ...(subtype === 'privateCredit' && { interestAccrued: day.sumInterestAccruedByPeriod }), + ...(subtype === 'privateCredit' && { assetWriteOffs: day.sumDebtWrittenOffByPeriod }), + otherPayments, + ...(subtype === 'publicCredit' && { totalIncome }), + fees, + totalExpenses, + totalProfitAndLoss, + } as ProfitAndLossReport + }) + return this.applyGrouping(items, filter?.groupBy, 'sum') + } + + /** + * Apply grouping to a report. + * @param items Report items + * @param filter Optional filtering and grouping options + * @param strategy Grouping strategy, sum aggregates data by period, latest returns the latest item in the period + * @returns Grouped report + * + * Note: if strategy is 'sum', only Currency values that are not nested are aggregated, all + * other values are overwritten with the last value in the period + */ + private applyGrouping< + T extends { + timestamp: string + [key: string]: Currency | string | { [key: string]: any } | undefined + }, + >(items: T[], groupBy: ReportFilter['groupBy'] = 'day', strategy: 'latest' | 'sum' = 'latest'): T[] { + if (strategy === 'latest') { + return groupByPeriod(items, groupBy, 'latest') + } + + const groups = groupByPeriod(items, groupBy, 'all') + return groups.map((group) => { + const base = { ...group[group.length - 1] } as T + + // Aggregate Currency values + for (const key in base) { + const value = base[key as keyof T] + if (value instanceof Currency) { + base[key as keyof T] = group.reduce( + (sum, item) => sum.add(item[key as keyof T] as Currency), + new Currency(0n, value.decimals) + ) as T[keyof T] + } + } + return base + }) + } + + private getDateKey(timestamp: string, groupBy?: ReportFilter['groupBy']): string { + switch (groupBy) { + case 'month': + return timestamp.slice(0, 7) // YYYY-MM + case 'quarter': + const date = new Date(timestamp) + const quarter = Math.floor(date.getMonth() / 3) + 1 + return `${date.getFullYear()}-Q${quarter}` // YYYY-Q# + case 'year': + return timestamp.slice(0, 4) // YYYY + default: + return timestamp.slice(0, 10) // YYYY-MM-DD + } + } +} + +export const processor = new Processor() diff --git a/src/Reports/Reports.test.ts b/src/Reports/Reports.test.ts index 035f66e..8d0c1f3 100644 --- a/src/Reports/Reports.test.ts +++ b/src/Reports/Reports.test.ts @@ -1,53 +1,177 @@ import { expect } from 'chai' import { Centrifuge } from '../Centrifuge.js' import { spy } from 'sinon' -import { ReportFilter, Reports } from '../Reports/index.js' -import * as balanceSheetProcessor from '../Reports/processors/balanceSheet.js' -import { firstValueFrom } from 'rxjs' +import { Reports } from '../Reports/index.js' +import { ReportFilter } from './types.js' +import { processor } from './Processor.js' describe('Reports', () => { let centrifuge: Centrifuge - before(async () => { + before(() => { centrifuge = new Centrifuge({ environment: 'mainnet', indexerUrl: 'https://subql.embrio.tech/', }) }) - it('should get balance sheet report', async () => { - const ns3PoolId = '1615768079' - const pool = await centrifuge.pool(ns3PoolId) - const balanceSheetReport = await pool.reports.balanceSheet({ - from: '2024-11-03T22:11:29.776Z', - to: '2024-11-06T22:11:29.776Z', - groupBy: 'day', + describe('balance sheet report', () => { + it('should fetch balance sheet report', async () => { + const processBalanceSheetSpy = spy(processor, 'balanceSheet') + const ns3PoolId = '1615768079' + const pool = await centrifuge.pool(ns3PoolId) + const balanceSheetReport = await pool.reports.balanceSheet({ + from: '2024-11-02T22:11:29.776Z', + to: '2024-11-06T22:11:29.776Z', + groupBy: 'day', + }) + expect(balanceSheetReport.length).to.be.eql(4) + expect(balanceSheetReport?.[0]?.tranches?.length ?? 0).to.be.eql(2) // ns3 has 2 tranches + expect(balanceSheetReport?.[0]?.tranches?.[0]?.timestamp.slice(0, 10)).to.be.eql( + balanceSheetReport?.[0]?.timestamp.slice(0, 10) + ) + expect(processBalanceSheetSpy.callCount).to.equal(1) + processBalanceSheetSpy.restore() }) - expect(balanceSheetReport.length).to.be.eql(3) - expect(balanceSheetReport?.[0]?.tranches?.length ?? 0).to.be.eql(2) // ns3 has 2 tranches - expect(balanceSheetReport?.[0]?.tranches?.[0]?.timestamp.slice(0, 10)).to.be.eql( - balanceSheetReport?.[0]?.date.slice(0, 10) - ) - }) - it('should use cached data for repeated queries', async () => { - const processBalanceSheetSpy = spy(balanceSheetProcessor, 'processBalanceSheetData') + it('should use cached data for repeated queries', async () => { + const processBalanceSheetSpy = spy(processor, 'balanceSheet') + const ns3PoolId = '1615768079' + const reports = new Reports(centrifuge, ns3PoolId) + + const filter: ReportFilter = { + from: '2024-11-03T22:11:29.776Z', + to: '2024-11-06T22:11:29.776Z', + groupBy: 'day', + } + + await reports.balanceSheet(filter) + expect(processBalanceSheetSpy.callCount).to.equal(1) + + // Same query should use cache + await reports.balanceSheet(filter) + expect(processBalanceSheetSpy.callCount).to.equal(1) + + processBalanceSheetSpy.restore() + }) + + it('should fetch new data for different query', async () => { + const processBalanceSheetSpy = spy(processor, 'balanceSheet') + const ns3PoolId = '1615768079' + const reports = new Reports(centrifuge, ns3PoolId) + + const filter: ReportFilter = { + from: '2024-10-03T22:11:29.776Z', + to: '2024-10-06T22:11:29.776Z', + groupBy: 'day', + } + + const report = await reports.balanceSheet(filter) + expect(processBalanceSheetSpy.callCount).to.equal(1) + expect(report.length).to.equal(3) - const ns3PoolId = '1615768079' - const reports = new Reports(centrifuge, ns3PoolId) + // Different query should fetch new data + const report2 = await reports.balanceSheet({ ...filter, to: '2024-10-10T22:11:29.776Z' }) - const filter: ReportFilter = { - from: '2024-11-03T22:11:29.776Z', - to: '2024-11-06T22:11:29.776Z', - groupBy: 'day', - } + expect(processBalanceSheetSpy.callCount).to.equal(2) + expect(report2.length).to.equal(7) - await firstValueFrom(reports.balanceSheet(filter)) - expect(processBalanceSheetSpy.callCount).to.equal(1) + const report3 = await reports.balanceSheet({ ...filter, groupBy: 'month' }) + + expect(processBalanceSheetSpy.callCount).to.equal(3) + expect(report3.length).to.equal(1) + + processBalanceSheetSpy.restore() + }) - // Same query should use cache - await firstValueFrom(reports.balanceSheet(filter)) - // TODO: Can't spy on es module - expect(processBalanceSheetSpy.callCount).to.equal(1) + it('should retrieve 6 months worth of data and group by day, month, quarter and year', async () => { + const anemoyPoolId = '4139607887' + const reports = new Reports(centrifuge, anemoyPoolId) + let filter: ReportFilter = { + from: '2024-01-01', + to: '2024-06-30', + groupBy: 'day', + } + const report = await reports.balanceSheet(filter) + expect(report.length).to.equal(181) + + filter = { + ...filter, + groupBy: 'month', + } + const report2 = await reports.balanceSheet(filter) + expect(report2.length).to.equal(6) + expect(report?.[report.length - 1]?.timestamp.slice(0, 10)).to.equal('2024-06-30') + expect(report?.[report.length - 1]?.netAssetValue.toString()).to.equal('11865636571724') // real data + + filter = { + ...filter, + groupBy: 'quarter', + } + const report3 = await reports.balanceSheet(filter) + expect(report3.length).to.equal(2) + + filter = { + ...filter, + groupBy: 'year', + } + const report4 = await reports.balanceSheet(filter) + expect(report4.length).to.equal(1) + expect(report4?.[0]?.timestamp.slice(0, 10)).to.equal('2024-06-30') + }) + }) + + describe('cashflow report', () => { + it('should fetch cashflow report', async () => { + const processCashflowSpy = spy(processor, 'cashflow') + const ltfPoolId = '4139607887' + const lftMetadataHash = 'QmTjbzx4mX1A9vRFxzLDZszKQSTsFbH8YgnpfmTSfWx73G' + const pool = await centrifuge.pool(ltfPoolId, lftMetadataHash) + const cashflowReport = await pool.reports.cashflow({ + from: '2024-11-02T22:11:29.776Z', + to: '2024-11-06T22:11:29.776Z', + groupBy: 'day', + }) + expect(cashflowReport.length).to.be.eql(4) + expect(processCashflowSpy.callCount).to.equal(1) + expect(cashflowReport?.[0]?.timestamp.slice(0, 10)).to.be.eq( + cashflowReport?.[0]?.fees?.[0]?.timestamp.slice(0, 10) + ) + }) + it('should retrieve 6 months worth of data and group by day, month, quarter and year', async () => { + const anemoyPoolId = '4139607887' + const reports = new Reports(centrifuge, anemoyPoolId) + let filter: ReportFilter = { + from: '2024-01-01', + to: '2024-06-30', + groupBy: 'day', + } + const report = await reports.cashflow(filter) + expect(report.length).to.equal(181) + expect(report?.[report.length - 1]?.timestamp.slice(0, 10)).to.equal('2024-06-30') + + filter = { + ...filter, + groupBy: 'month', + } + const report2 = await reports.cashflow(filter) + expect(report2.length).to.equal(6) + expect(report2?.[report2.length - 1]?.totalCashflow.toString()).to.equal('-8374299271') // real data + + filter = { + ...filter, + groupBy: 'quarter', + } + const report3 = await reports.cashflow(filter) + expect(report3.length).to.equal(2) + + filter = { + ...filter, + groupBy: 'year', + } + const report4 = await reports.cashflow(filter) + expect(report4.length).to.equal(1) + expect(report4?.[0]?.timestamp.slice(0, 10)).to.equal('2024-06-30') + }) }) }) diff --git a/src/Reports/index.ts b/src/Reports/index.ts index 9e1e407..3f0133c 100644 --- a/src/Reports/index.ts +++ b/src/Reports/index.ts @@ -6,65 +6,83 @@ import { trancheSnapshotsPostProcess, trancheSnapshotsQuery, } from '../queries/trancheSnapshots.js' -import { processBalanceSheetData } from './processors/balanceSheet.js' import { combineLatest, of } from 'rxjs' -import { ReportProcessorType } from './processors/index.js' +import { processor } from './Processor.js' import { map } from 'rxjs' -import { GroupBy } from '../utils/date.js' -export interface ReportFilter { - from?: string - to?: string - groupBy?: GroupBy -} +import { BalanceSheetReport, CashflowReport, ReportFilter } from './types.js' +import { Query } from '../types/query.js' +import { + PoolFeeSnapshotFilter, + poolFeeSnapshotQuery, + poolFeeSnapshotsPostProcess, +} from '../queries/poolFeeSnapshots.js' +import { PoolMetadata } from '../types/poolMetadata.js' -export interface ReportData { - timestamp: string - [key: string]: unknown -} +type ReportType = 'balanceSheet' | 'cashflow' export class Reports extends Entity { constructor( centrifuge: Centrifuge, - public poolId: string + public poolId: string, + public metadataHash?: string ) { super(centrifuge, ['reports', poolId]) } + // TODO: think about a horizontal formatting option balanceSheet(filter?: ReportFilter) { - return this.generateReport('balanceSheet', filter) + return this._generateReport('balanceSheet', filter) } - generateReport(type: ReportProcessorType, filter?: ReportFilter) { - return this._root._query( - [type, ...(filter ? [filter] : [])], + cashflow(filter?: ReportFilter) { + return this._generateReport('cashflow', filter) + } + + _generateReport(type: ReportType, filter?: ReportFilter): Query { + return this._query( + [type, filter?.from, filter?.to, filter?.groupBy], () => { const dateFilter = { timestamp: { greaterThan: filter?.from, - lessThan: filter?.to, + lessThanOrEqualTo: filter?.to && `${filter.to.split('T')[0]}T23:59:59.999Z`, }, } - return combineLatest([ - this.poolSnapshots({ - ...dateFilter, - poolId: { equalTo: this.poolId }, - }), - this.trancheSnapshots({ - ...dateFilter, - tranche: { poolId: { equalTo: this.poolId } }, - }), - ]).pipe( - map(([poolSnapshots, trancheSnapshots]) => { - switch (type) { - case 'balanceSheet': - return processBalanceSheetData({ poolSnapshots, trancheSnapshots }, filter) - default: - throw new Error(`Unsupported report type: ${type}`) - } - }) - ) + const metadata$ = this.metadataHash ? this._root._queryIPFS(this.metadataHash) : of(undefined) + + const poolSnapshots$ = this.poolSnapshots({ + ...dateFilter, + poolId: { equalTo: this.poolId }, + }) + const trancheSnapshots$ = this.trancheSnapshots({ + ...dateFilter, + tranche: { poolId: { equalTo: this.poolId } }, + }) + const poolFeeSnapshots$ = this.poolFeeSnapshots({ + ...dateFilter, + poolFeeId: { includes: this.poolId }, + }) + + switch (type) { + case 'balanceSheet': + return combineLatest([poolSnapshots$, trancheSnapshots$]).pipe( + map( + ([poolSnapshots, trancheSnapshots]) => + processor.balanceSheet({ poolSnapshots, trancheSnapshots }, filter) as T[] + ) + ) + case 'cashflow': + return combineLatest([poolSnapshots$, poolFeeSnapshots$, metadata$]).pipe( + map( + ([poolSnapshots, poolFeeSnapshots, metadata]) => + processor.cashflow({ poolSnapshots, poolFeeSnapshots, metadata }, filter) as T[] + ) + ) + default: + throw new Error(`Unsupported report type: ${type}`) + } }, { valueCacheTime: 120, @@ -72,6 +90,10 @@ export class Reports extends Entity { ) } + poolFeeSnapshots(filter?: PoolFeeSnapshotFilter) { + return this._root._queryIndexer(poolFeeSnapshotQuery, { filter }, poolFeeSnapshotsPostProcess) + } + poolSnapshots(filter?: PoolSnapshotFilter) { return this._root._queryIndexer(poolSnapshotsQuery, { filter }, poolSnapshotsPostProcess) } @@ -79,8 +101,4 @@ export class Reports extends Entity { trancheSnapshots(filter?: TrancheSnapshotFilter) { return this._root._queryIndexer(trancheSnapshotsQuery, { filter }, trancheSnapshotsPostProcess) } - - _processBalanceSheet(data: any, filter?: ReportFilter) { - return processBalanceSheetData(data, filter) - } } diff --git a/src/Reports/processors/balanceSheet.ts b/src/Reports/processors/balanceSheet.ts deleted file mode 100644 index 77fd010..0000000 --- a/src/Reports/processors/balanceSheet.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { PoolSnapshot } from '../../queries/poolSnapshots.js' -import { TrancheSnapshot } from '../../queries/trancheSnapshots.js' -import { Currency, Price } from '../../utils/BigInt.js' -import { groupByPeriod } from '../../utils/date.js' -import { ReportData, ReportFilter } from '../types.js' - -export interface BalanceSheetReport extends ReportData { - date: string - assetValuation: Currency - onchainReserve: Currency - offchainCash: Currency - accruedFees: Currency - netAssetValue: Currency - tranches?: { - name: string - timestamp: string - tokenId: string - tokenSupply: Currency - tokenPrice: Price | null - trancheValue: Currency - }[] - totalCapital?: Currency -} - -type BalanceSheetData = { - poolSnapshots: PoolSnapshot[] - trancheSnapshots: TrancheSnapshot[] -} - -export function processBalanceSheetData(data: BalanceSheetData, filter?: ReportFilter): BalanceSheetReport[] { - const trancheSnapshotsByDate = groupTranchesByDate(data.trancheSnapshots) - const items = data?.poolSnapshots?.map((snapshot) => { - const tranches = trancheSnapshotsByDate.get(snapshot.timestamp.slice(0, 10)) ?? [] - return { - timestamp: snapshot.timestamp, - date: snapshot.timestamp, - assetValuation: snapshot.portfolioValuation, - onchainReserve: snapshot.totalReserve, - offchainCash: snapshot.offchainCashValue, - accruedFees: snapshot.sumPoolFeesPendingAmount, - netAssetValue: snapshot.netAssetValue, - tranches: tranches?.map((tranche) => ({ - name: tranche.pool.currency.symbol, - timestamp: tranche.timestamp, - tokenId: tranche.trancheId, - tokenSupply: tranche.tokenSupply, - tokenPrice: tranche.price, - trancheValue: tranche.tokenSupply.mul(tranche?.price?.toBigInt() ?? 0n), - })), - totalCapital: tranches.reduce( - (acc, curr) => acc.add(curr.tokenSupply.mul(curr?.price?.toBigInt() ?? 0n).toBigInt()), - new Currency(0, snapshot.poolCurrency.decimals) - ), - } - }) - return filter?.groupBy ? groupByPeriod(items, filter.groupBy) : items -} - -function groupTranchesByDate(trancheSnapshots: TrancheSnapshot[]): Map { - const grouped = new Map() - if (!trancheSnapshots) return grouped - trancheSnapshots?.forEach((snapshot) => { - const date = snapshot.timestamp.slice(0, 10) - if (!grouped.has(date)) { - grouped.set(date, []) - } - grouped.get(date)!.push(snapshot) - }) - return grouped -} diff --git a/src/Reports/processors/index.ts b/src/Reports/processors/index.ts deleted file mode 100644 index 1d5e23e..0000000 --- a/src/Reports/processors/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { processBalanceSheetData } from './balanceSheet.js' - -export const processors = { - balanceSheet: processBalanceSheetData, -} as const - -export type ReportProcessorType = keyof typeof processors diff --git a/src/Reports/types.ts b/src/Reports/types.ts index d4c42c9..eaa8ec5 100644 --- a/src/Reports/types.ts +++ b/src/Reports/types.ts @@ -1,3 +1,9 @@ +import { PoolFeeSnapshotsByDate } from '../queries/poolFeeSnapshots.js' +import { PoolSnapshot } from '../queries/poolSnapshots.js' +import { TrancheSnapshotsByDate } from '../queries/trancheSnapshots.js' +import { PoolMetadata } from '../types/poolMetadata.js' +import { Price } from '../utils/BigInt.js' +import { Currency } from '../utils/BigInt.js' import { GroupBy } from '../utils/date.js' export interface ReportFilter { @@ -6,7 +12,89 @@ export interface ReportFilter { groupBy?: GroupBy } -export interface ReportData { +export type BalanceSheetReport = { + type: 'balanceSheet' timestamp: string - [key: string]: unknown + assetValuation: Currency + onchainReserve: Currency + offchainCash: Currency + accruedFees: Currency + netAssetValue: Currency + tranches?: { + name: string + timestamp: string + tokenId: string + tokenSupply: Currency + tokenPrice: Price | null + trancheValue: Currency + }[] + totalCapital?: Currency +} + +export type BalanceSheetData = { + poolSnapshots: PoolSnapshot[] + trancheSnapshots: TrancheSnapshotsByDate +} + +type CashflowReportBase = { + type: 'cashflow' + timestamp: string + principalPayments: Currency + interestPayments: Currency + assetAcquisitions: Currency + netCashflowAsset: Currency // sum of cashflow from assetAcquisitions, principalPayments, interestPayments, realizedPL + fees: { name: string; amount: Currency; timestamp: string; feeId: string }[] + netCashflowAfterFees: Currency + investments: Currency + redemptions: Currency + activitiesCashflow: Currency // sum of cashflow from investments and redemptions + totalCashflow: Currency // sum of netCashflowAsset, netCashflowAfterFees and activitiesCashflow + endCashBalance: { balance: Currency } +} + +type CashflowReportPublicCredit = CashflowReportBase & { + subtype: 'publicCredit' + realizedPL?: Currency +} + +type CashflowReportPrivateCredit = CashflowReportBase & { + subtype: 'privateCredit' +} + +export type CashflowReport = CashflowReportPublicCredit | CashflowReportPrivateCredit + +export type CashflowData = { + poolSnapshots: PoolSnapshot[] + poolFeeSnapshots: PoolFeeSnapshotsByDate + metadata: PoolMetadata | undefined +} + +export type ProfitAndLossReportBase = { + type: 'profitAndLoss' + timestamp: string + profitAndLossFromAsset: Currency + interestPayments: Currency + otherPayments: Currency + totalExpenses: Currency + totalProfitAndLoss: Currency + fees: { name: string; amount: Currency; timestamp: string; feeId: string }[] +} + +export type ProfitAndLossReportPublicCredit = ProfitAndLossReportBase & { + subtype: 'publicCredit' + totalIncome: Currency +} + +export type ProfitAndLossReportPrivateCredit = ProfitAndLossReportBase & { + subtype: 'privateCredit' + interestAccrued: Currency + assetWriteOffs: Currency +} + +export type ProfitAndLossReport = ProfitAndLossReportPublicCredit | ProfitAndLossReportPrivateCredit + +export type ProfitAndLossData = { + poolSnapshots: PoolSnapshot[] + poolFeeSnapshots: PoolFeeSnapshotsByDate + metadata: PoolMetadata | undefined } diff --git a/src/queries/poolFeeSnapshots.ts b/src/queries/poolFeeSnapshots.ts new file mode 100644 index 0000000..e235644 --- /dev/null +++ b/src/queries/poolFeeSnapshots.ts @@ -0,0 +1,109 @@ +import { Currency } from '../utils/BigInt.js' + +export type PoolFeeSnapshotFilter = Partial> + +export type PoolFeeSnapshot = { + pendingAmount: Currency + poolFee: { name: string } + poolFeeId: string + sumAccruedAmount: Currency + sumChargedAmount: Currency + sumPaidAmount: Currency + sumAccruedAmountByPeriod: Currency + sumChargedAmountByPeriod: Currency + sumPaidAmountByPeriod: Currency + timestamp: string + poolCurrency: { + decimals: number + } +} + +export type PoolFeeSnapshotsByDate = { [timestamp: string]: PoolFeeSnapshot[] } + +export type SubqueryPoolFeeSnapshot = { + poolFeeSnapshots: { + nodes: { + poolFeeId: string // poolId-feeId + timestamp: string + sumPaidAmount: string + sumChargedAmount: string + sumAccruedAmount: string + pendingAmount: string + sumPaidAmountByPeriod: string + sumChargedAmountByPeriod: string + sumAccruedAmountByPeriod: string + poolFee: { + name: string + pool: { + currency: { + decimals: number + } + } + } + }[] + } +} + +export function poolFeeSnapshotsPostProcess(data: SubqueryPoolFeeSnapshot): PoolFeeSnapshotsByDate { + const poolFeeSnapshots = data.poolFeeSnapshots + const poolFeesGroupedByDate = poolFeeSnapshots?.nodes.reduce((acc, snapshot) => { + const date = snapshot.timestamp.slice(0, 10) + if (!acc[date]) { + acc[date] = [] + } + + const poolCurrencyDecimals = snapshot.poolFee.pool.currency.decimals + const poolFeeSnapshot: PoolFeeSnapshot = { + timestamp: snapshot.timestamp, + pendingAmount: new Currency(snapshot.pendingAmount, poolCurrencyDecimals), + poolFee: { + name: snapshot.poolFee.name, + }, + poolFeeId: snapshot.poolFeeId, + poolCurrency: { + decimals: poolCurrencyDecimals, + }, + sumAccruedAmount: new Currency(snapshot.sumAccruedAmount, poolCurrencyDecimals), + sumChargedAmount: new Currency(snapshot.sumChargedAmount, poolCurrencyDecimals), + sumPaidAmount: new Currency(snapshot.sumPaidAmount, poolCurrencyDecimals), + sumAccruedAmountByPeriod: new Currency(snapshot.sumAccruedAmountByPeriod, poolCurrencyDecimals), + sumChargedAmountByPeriod: new Currency(snapshot.sumChargedAmountByPeriod, poolCurrencyDecimals), + sumPaidAmountByPeriod: new Currency(snapshot.sumPaidAmountByPeriod, poolCurrencyDecimals), + } + + acc[date].push(poolFeeSnapshot) + return acc + }, {} as PoolFeeSnapshotsByDate) + return poolFeesGroupedByDate +} + +export const poolFeeSnapshotQuery = ` +query($filter: PoolFeeSnapshotFilter) { + poolFeeSnapshots( + orderBy: BLOCK_NUMBER_ASC, + filter: $filter + ) { + nodes { + id + poolFeeId + timestamp + sumPaidAmount + sumChargedAmount + sumAccruedAmount + sumPaidAmount + pendingAmount + sumAccruedAmountByPeriod + sumPaidAmountByPeriod + sumChargedAmountByPeriod + poolFee { + name + pool { + currency { + decimals + } + } + } + } + } +} +` diff --git a/src/queries/poolSnapshots.ts b/src/queries/poolSnapshots.ts index dbec0b2..de8b181 100644 --- a/src/queries/poolSnapshots.ts +++ b/src/queries/poolSnapshots.ts @@ -120,11 +120,11 @@ export function poolSnapshotsPostProcess(data: SubqueryPoolSnapshot): PoolSnapsh const timestamp = state.timestamp.slice(0, 10) const poolCurrencyDecimals = state.pool.currency.decimals // point in time snapshot used to aggregate snapshots by day (there can be multiple snapshots per day) - const snapshotToday = snapshotByDay.get(timestamp) + const currentSnapshot = snapshotByDay.get(timestamp) const poolState = { id: state.id, - timestamp, + timestamp: state.timestamp, poolId: state.pool.id, poolCurrency: state.pool.currency, netAssetValue: new Currency(state.netAssetValue, poolCurrencyDecimals), @@ -154,59 +154,59 @@ export function poolSnapshotsPostProcess(data: SubqueryPoolSnapshot): PoolSnapsh sumUnrealizedProfitByPeriod: new Currency(state.sumUnrealizedProfitByPeriod, poolCurrencyDecimals), } - if (snapshotToday) { - snapshotToday.sumPoolFeesChargedAmountByPeriod = snapshotToday.sumPoolFeesChargedAmountByPeriod.add( + if (currentSnapshot) { + currentSnapshot.sumPoolFeesChargedAmountByPeriod = currentSnapshot.sumPoolFeesChargedAmountByPeriod.add( poolState.sumPoolFeesChargedAmountByPeriod ) - snapshotToday.sumPoolFeesAccruedAmountByPeriod = snapshotToday.sumPoolFeesAccruedAmountByPeriod.add( + currentSnapshot.sumPoolFeesAccruedAmountByPeriod = currentSnapshot.sumPoolFeesAccruedAmountByPeriod.add( poolState.sumPoolFeesAccruedAmountByPeriod ) - snapshotToday.sumPoolFeesPaidAmountByPeriod = snapshotToday.sumPoolFeesPaidAmountByPeriod.add( + currentSnapshot.sumPoolFeesPaidAmountByPeriod = currentSnapshot.sumPoolFeesPaidAmountByPeriod.add( poolState.sumPoolFeesPaidAmountByPeriod ) - snapshotToday.sumBorrowedAmountByPeriod = snapshotToday.sumBorrowedAmountByPeriod.add( + currentSnapshot.sumBorrowedAmountByPeriod = currentSnapshot.sumBorrowedAmountByPeriod.add( poolState.sumBorrowedAmountByPeriod ) - snapshotToday.sumPrincipalRepaidAmountByPeriod = snapshotToday.sumPrincipalRepaidAmountByPeriod.add( + currentSnapshot.sumPrincipalRepaidAmountByPeriod = currentSnapshot.sumPrincipalRepaidAmountByPeriod.add( poolState.sumPrincipalRepaidAmountByPeriod ) - snapshotToday.sumInterestRepaidAmountByPeriod = snapshotToday.sumInterestRepaidAmountByPeriod.add( + currentSnapshot.sumInterestRepaidAmountByPeriod = currentSnapshot.sumInterestRepaidAmountByPeriod.add( poolState.sumInterestRepaidAmountByPeriod ) - snapshotToday.sumUnscheduledRepaidAmountByPeriod = snapshotToday.sumUnscheduledRepaidAmountByPeriod.add( + currentSnapshot.sumUnscheduledRepaidAmountByPeriod = currentSnapshot.sumUnscheduledRepaidAmountByPeriod.add( poolState.sumUnscheduledRepaidAmountByPeriod ) - snapshotToday.sumRepaidAmountByPeriod = snapshotToday.sumRepaidAmountByPeriod.add( + currentSnapshot.sumRepaidAmountByPeriod = currentSnapshot.sumRepaidAmountByPeriod.add( poolState.sumRepaidAmountByPeriod ) - snapshotToday.sumInvestedAmountByPeriod = snapshotToday.sumInvestedAmountByPeriod.add( + currentSnapshot.sumInvestedAmountByPeriod = currentSnapshot.sumInvestedAmountByPeriod.add( poolState.sumInvestedAmountByPeriod ) - snapshotToday.sumRedeemedAmountByPeriod = snapshotToday.sumRedeemedAmountByPeriod.add( + currentSnapshot.sumRedeemedAmountByPeriod = currentSnapshot.sumRedeemedAmountByPeriod.add( poolState.sumRedeemedAmountByPeriod ) - snapshotToday.sumDebtWrittenOffByPeriod = snapshotToday.sumDebtWrittenOffByPeriod.add( + currentSnapshot.sumDebtWrittenOffByPeriod = currentSnapshot.sumDebtWrittenOffByPeriod.add( poolState.sumDebtWrittenOffByPeriod ) - snapshotToday.sumInterestAccruedByPeriod = snapshotToday.sumInterestAccruedByPeriod.add( + currentSnapshot.sumInterestAccruedByPeriod = currentSnapshot.sumInterestAccruedByPeriod.add( poolState.sumInterestAccruedByPeriod ) - snapshotToday.sumRealizedProfitFifoByPeriod = snapshotToday.sumRealizedProfitFifoByPeriod.add( + currentSnapshot.sumRealizedProfitFifoByPeriod = currentSnapshot.sumRealizedProfitFifoByPeriod.add( poolState.sumRealizedProfitFifoByPeriod ) - snapshotToday.sumUnrealizedProfitByPeriod = snapshotToday.sumUnrealizedProfitByPeriod.add( + currentSnapshot.sumUnrealizedProfitByPeriod = currentSnapshot.sumUnrealizedProfitByPeriod.add( poolState.sumUnrealizedProfitByPeriod ) // Update point-in-time values with latest - snapshotToday.netAssetValue = poolState.netAssetValue - snapshotToday.totalReserve = poolState.totalReserve - snapshotToday.offchainCashValue = poolState.offchainCashValue - snapshotToday.portfolioValuation = poolState.portfolioValuation - snapshotToday.sumPoolFeesPendingAmount = poolState.sumPoolFeesPendingAmount - snapshotToday.sumUnrealizedProfitAtMarketPrice = poolState.sumUnrealizedProfitAtMarketPrice - snapshotToday.sumUnrealizedProfitAtNotional = poolState.sumUnrealizedProfitAtNotional + currentSnapshot.netAssetValue = poolState.netAssetValue + currentSnapshot.totalReserve = poolState.totalReserve + currentSnapshot.offchainCashValue = poolState.offchainCashValue + currentSnapshot.portfolioValuation = poolState.portfolioValuation + currentSnapshot.sumPoolFeesPendingAmount = poolState.sumPoolFeesPendingAmount + currentSnapshot.sumUnrealizedProfitAtMarketPrice = poolState.sumUnrealizedProfitAtMarketPrice + currentSnapshot.sumUnrealizedProfitAtNotional = poolState.sumUnrealizedProfitAtNotional return [] } diff --git a/src/queries/trancheSnapshots.ts b/src/queries/trancheSnapshots.ts index caa094d..7fd85d2 100644 --- a/src/queries/trancheSnapshots.ts +++ b/src/queries/trancheSnapshots.ts @@ -101,22 +101,25 @@ export type TrancheSnapshot = { yieldSinceLastPeriod: Perquintill | null } -export type GroupedTrancheSnapshots = { +export type TrancheSnapshotsByDate = { [timestamp: string]: TrancheSnapshot[] } -export function trancheSnapshotsPostProcess(data: SubqueryTrancheSnapshot): TrancheSnapshot[] { - const tranches: { [key: string]: TrancheSnapshot } = {} +export function trancheSnapshotsPostProcess(data: SubqueryTrancheSnapshot): { [date: string]: TrancheSnapshot[] } { + const tranchesByDate: TrancheSnapshotsByDate = {} + data?.trancheSnapshots?.nodes?.forEach((tranche) => { - const tid = tranche.tranche.trancheId const date = tranche.timestamp.slice(0, 10) - const key = `${date}-${tid}` + if (!tranchesByDate[date]) { + tranchesByDate[date] = [] + } + const poolCurrency = tranche.tranche.pool.currency - tranches[key] = { + const trancheSnapshot: TrancheSnapshot = { id: tranche.trancheId, timestamp: tranche.timestamp, poolId: tranche.tranche.poolId, - trancheId: tid, + trancheId: tranche.tranche.trancheId, pool: { currency: { decimals: poolCurrency.decimals, @@ -138,6 +141,9 @@ export function trancheSnapshotsPostProcess(data: SubqueryTrancheSnapshot): Tran yieldYTD: new Perquintill(tranche.yieldYTD ?? 0), yieldSinceLastPeriod: new Perquintill(tranche.yieldSinceLastPeriod ?? 0), } + + tranchesByDate[date].push(trancheSnapshot) }) - return Object.values(tranches) + + return tranchesByDate } diff --git a/src/tests/mocks/mockPoolFeeSnapshot.ts b/src/tests/mocks/mockPoolFeeSnapshot.ts new file mode 100644 index 0000000..5e43244 --- /dev/null +++ b/src/tests/mocks/mockPoolFeeSnapshot.ts @@ -0,0 +1,69 @@ +import { Currency } from '../../utils/BigInt.js' +import { PoolFeeSnapshotsByDate } from '../../queries/poolFeeSnapshots.js' + +export const mockPoolFeeSnapshots: PoolFeeSnapshotsByDate = { + '2024-01-01': [ + { + pendingAmount: new Currency(0n, 6), + poolFee: { name: 'poolFee 1' }, + poolFeeId: 'poolFee-1', + sumAccruedAmount: new Currency(0n, 6), + sumChargedAmount: new Currency(0n, 6), + sumPaidAmount: new Currency(0n, 6), + sumAccruedAmountByPeriod: new Currency(0n, 6), + sumChargedAmountByPeriod: new Currency(0n, 6), + sumPaidAmountByPeriod: new Currency(0n, 6), + timestamp: '2024-01-01T12:00:00Z', + poolCurrency: { + decimals: 6, + }, + }, + { + pendingAmount: new Currency(0n, 6), + poolFee: { name: 'poolFee 2' }, + poolFeeId: 'poolFee-2', + sumAccruedAmount: new Currency(0n, 6), + sumChargedAmount: new Currency(0n, 6), + sumPaidAmount: new Currency(0n, 6), + sumAccruedAmountByPeriod: new Currency(0n, 6), + sumChargedAmountByPeriod: new Currency(0n, 6), + sumPaidAmountByPeriod: new Currency(0n, 6), + timestamp: '2024-01-01T12:00:00Z', + poolCurrency: { + decimals: 6, + }, + }, + ], + '2024-01-02': [ + { + pendingAmount: new Currency(0n, 6), + poolFee: { name: 'poolFee 1' }, + poolFeeId: 'poolFee-1', + sumAccruedAmount: new Currency(0n, 6), + sumChargedAmount: new Currency(0n, 6), + sumPaidAmount: new Currency(0n, 6), + sumAccruedAmountByPeriod: new Currency(0n, 6), + sumChargedAmountByPeriod: new Currency(0n, 6), + sumPaidAmountByPeriod: new Currency(0n, 6), + timestamp: '2024-01-02T12:00:00Z', + poolCurrency: { + decimals: 6, + }, + }, + { + pendingAmount: new Currency(0n, 6), + poolFee: { name: 'poolFee 2' }, + poolFeeId: 'poolFee-2', + sumAccruedAmount: new Currency(0n, 6), + sumChargedAmount: new Currency(0n, 6), + sumPaidAmount: new Currency(0n, 6), + sumAccruedAmountByPeriod: new Currency(0n, 6), + sumChargedAmountByPeriod: new Currency(0n, 6), + sumPaidAmountByPeriod: new Currency(0n, 6), + timestamp: '2024-01-02T12:00:00Z', + poolCurrency: { + decimals: 6, + }, + }, + ], +} diff --git a/src/tests/mocks/mockPoolMetadata.ts b/src/tests/mocks/mockPoolMetadata.ts new file mode 100644 index 0000000..108c321 --- /dev/null +++ b/src/tests/mocks/mockPoolMetadata.ts @@ -0,0 +1,119 @@ +import { PoolMetadata } from '../../types/poolMetadata.js' + +export const mockPoolMetadata: PoolMetadata = { + version: 1, + pool: { + name: 'Fake Pool', + icon: { + uri: 'ipfs://QmVxA2MaBMUsZMgxBcfg5VdrF4FJAVW26U9s3mY9oH7wMs', + mime: 'image/svg+xml', + }, + asset: { + class: 'Private credit', + subClass: 'Fake Subclass', + }, + issuer: { + name: 'Fake Issuer', + repName: '', + description: 'Fake Description', + email: 'fake@issuer.com', + logo: { + uri: 'ipfs://QmZB95usKnoSHEwG9s61gYWqgxbYyq7NAdo24XEowqJtFS', + mime: 'image/png', + }, + shortDescription: 'Fake Short Description', + categories: [ + { + type: 'trustee', + value: 'Fake Trustee', + customType: '', + }, + { + type: 'historicalDefaultRate', + value: '0', + customType: '', + }, + ], + }, + links: { + executiveSummary: { + uri: 'ipfs://QmRQqb11SnqpSHwPcsgmgGWCv8GKfALgX53KLbvf6iur5Y', + mime: 'application/pdf', + }, + forum: 'https://gov.centrifuge.io/tag/newsilver', + website: 'https://newsilver.com', + }, + details: [], + status: 'open', + listed: true, + poolFees: [ + { + name: 'Protocol fee (Private Credit & Securities)', + id: 3, + feePosition: 'Top of waterfall', + feeType: 'fixed', + }, + ], + reports: [ + { + author: { + avatar: null, + name: 'Fake Author', + title: 'Fake Title', + }, + uri: 'https://gov.centrifuge.io/t/fake-report', + }, + ], + investorType: 'Fake Investor Type', + poolStructure: 'Fake Structure', + poolRatings: [], + }, + pod: { + indexer: 'https://fake-pod.com', + }, + tranches: { + '0x6756e091ae798a8e51e12e27ee8facdf': { + minInitialInvestment: '25000000000', + targetAPY: '16', + }, + '0xda64aae939e4d3a981004619f1709d8f': { + minInitialInvestment: '5000000000', + }, + }, + adminMultisig: { + signers: [ + '5F3quVAnmP1uc2v5zLyXLH3JxNLzXeHXDg5j9ScrvbvHGuJW', + '5FpnSw6TPjnebvxiQfainnoS5k8gC9h7F2AyQmbVyy6RhM9E', + '5GqHmRkfWnKrCgoR2yfdqgsgvQj9X4rfMb7jgP4Lca1f39s9', + ], + threshold: 2, + }, + onboarding: { + tranches: { + '0x6756e091ae798a8e51e12e27ee8facdf': { + agreement: { + uri: 'https://centrifuge.mypinata.cloud/ipfs/QmNbrzAHKKYtPCoY6g4WfxXnuWRdMdWRXm64LfQz5sYHAw', + mime: 'application/pdf', + }, + openForOnboarding: false, + }, + '0xda64aae939e4d3a981004619f1709d8f': { + agreement: { + uri: 'https://centrifuge.mypinata.cloud/ipfs/QmV3cXT38k45VeEYXQRfRPDza1wLf5pnjxy79D2RudMUrG', + mime: 'application/pdf', + }, + openForOnboarding: true, + }, + }, + kycRestrictedCountries: [], + kybRestrictedCountries: [], + podReadAccess: false, + taxInfoRequired: true, + }, + loanTemplates: [ + { + id: 'QmZ1FUqhaUxaRRT3czJJSseLZ1GqCFoQfgszRQybWkBTs1', + createdAt: '2024-07-03T17:12:11.404Z', + }, + ], +} diff --git a/src/tests/mocks/mockPoolSnapshots.ts b/src/tests/mocks/mockPoolSnapshots.ts new file mode 100644 index 0000000..792a950 --- /dev/null +++ b/src/tests/mocks/mockPoolSnapshots.ts @@ -0,0 +1,65 @@ +import { Currency } from '../../utils/BigInt.js' +import { PoolSnapshot } from '../../queries/poolSnapshots.js' + +export const mockPoolSnapshots: PoolSnapshot[] = [ + { + id: 'pool-10', + poolId: 'pool-1', + timestamp: '2024-01-01T12:00:00Z', + netAssetValue: new Currency(0n, 6), + totalReserve: new Currency(0n, 6), + offchainCashValue: new Currency(0n, 6), + portfolioValuation: new Currency(0n, 6), + sumPoolFeesChargedAmountByPeriod: new Currency(0n, 6), + sumPoolFeesAccruedAmountByPeriod: new Currency(0n, 6), + sumPoolFeesPaidAmountByPeriod: new Currency(0n, 6), + sumBorrowedAmountByPeriod: new Currency(0n, 6), + sumPrincipalRepaidAmountByPeriod: new Currency(0n, 6), + sumInterestRepaidAmountByPeriod: new Currency(0n, 6), + sumUnscheduledRepaidAmountByPeriod: new Currency(0n, 6), + sumRepaidAmountByPeriod: new Currency(0n, 6), + sumInvestedAmountByPeriod: new Currency(0n, 6), + sumRedeemedAmountByPeriod: new Currency(0n, 6), + sumPoolFeesPendingAmount: new Currency(0n, 6), + sumDebtWrittenOffByPeriod: new Currency(0n, 6), + sumInterestAccruedByPeriod: new Currency(0n, 6), + sumRealizedProfitFifoByPeriod: new Currency(0n, 6), + sumUnrealizedProfitAtMarketPrice: new Currency(0n, 6), + sumUnrealizedProfitAtNotional: new Currency(0n, 6), + sumUnrealizedProfitByPeriod: new Currency(0n, 6), + poolValue: new Currency(0n, 6), + poolCurrency: { + decimals: 6, + }, + }, + { + id: 'pool-11', + poolId: 'pool-1', + timestamp: '2024-01-02T12:00:00Z', + netAssetValue: new Currency(0n, 6), + totalReserve: new Currency(0n, 6), + offchainCashValue: new Currency(0n, 6), + portfolioValuation: new Currency(0n, 6), + sumPoolFeesChargedAmountByPeriod: new Currency(0n, 6), + sumPoolFeesAccruedAmountByPeriod: new Currency(0n, 6), + sumPoolFeesPaidAmountByPeriod: new Currency(0n, 6), + sumBorrowedAmountByPeriod: new Currency(0n, 6), + sumPrincipalRepaidAmountByPeriod: new Currency(0n, 6), + sumInterestRepaidAmountByPeriod: new Currency(0n, 6), + sumUnscheduledRepaidAmountByPeriod: new Currency(0n, 6), + sumRepaidAmountByPeriod: new Currency(0n, 6), + sumInvestedAmountByPeriod: new Currency(0n, 6), + sumRedeemedAmountByPeriod: new Currency(0n, 6), + sumPoolFeesPendingAmount: new Currency(0n, 6), + sumDebtWrittenOffByPeriod: new Currency(0n, 6), + sumInterestAccruedByPeriod: new Currency(0n, 6), + sumRealizedProfitFifoByPeriod: new Currency(0n, 6), + sumUnrealizedProfitAtMarketPrice: new Currency(0n, 6), + sumUnrealizedProfitAtNotional: new Currency(0n, 6), + sumUnrealizedProfitByPeriod: new Currency(0n, 6), + poolValue: new Currency(0n, 6), + poolCurrency: { + decimals: 6, + }, + }, +] diff --git a/src/tests/mocks/mockTrancheSnapshots.ts b/src/tests/mocks/mockTrancheSnapshots.ts new file mode 100644 index 0000000..8f0ae6a --- /dev/null +++ b/src/tests/mocks/mockTrancheSnapshots.ts @@ -0,0 +1,114 @@ +import { Price, Currency, Perquintill } from '../../utils/BigInt.js' + +import { TrancheSnapshotsByDate } from '../../queries/trancheSnapshots.js' + +export const mockTrancheSnapshots: TrancheSnapshotsByDate = { + '2024-01-01': [ + { + id: 'tranche-1', + price: Price.fromFloat('1.00'), + timestamp: '2024-01-01T00:00:00Z', + trancheId: 'senior', + poolId: 'pool-1', + tokenSupply: new Currency(0n, 6), + pool: { + currency: { + decimals: 6, + symbol: 'USDC', + }, + }, + outstandingInvestOrders: new Currency(0n, 6), + outstandingRedeemOrders: new Currency(0n, 6), + fulfilledInvestOrders: new Currency(0n, 6), + fulfilledRedeemOrders: new Currency(0n, 6), + yield7DaysAnnualized: null, + yield30DaysAnnualized: null, + yield90DaysAnnualized: null, + yieldSinceInception: new Perquintill(0n), + yieldMTD: null, + yieldQTD: null, + yieldYTD: null, + yieldSinceLastPeriod: null, + }, + { + id: 'tranche-2', + price: Price.fromFloat('1.12'), + timestamp: '2024-01-01T00:00:00Z', + trancheId: 'junior', + poolId: 'pool-1', + tokenSupply: new Currency(0n, 6), + pool: { + currency: { + decimals: 6, + symbol: 'USDC', + }, + }, + outstandingInvestOrders: new Currency(0n, 6), + outstandingRedeemOrders: new Currency(0n, 6), + fulfilledInvestOrders: new Currency(0n, 6), + fulfilledRedeemOrders: new Currency(0n, 6), + yield7DaysAnnualized: null, + yield30DaysAnnualized: null, + yield90DaysAnnualized: null, + yieldSinceInception: new Perquintill(0n), + yieldMTD: null, + yieldQTD: null, + yieldYTD: null, + yieldSinceLastPeriod: null, + }, + ], + '2024-01-02': [ + { + id: 'tranche-1', + price: Price.fromFloat('1.00'), + timestamp: '2024-01-02T00:00:00Z', + trancheId: 'senior', + poolId: 'pool-1', + tokenSupply: new Currency(0n, 6), + pool: { + currency: { + decimals: 6, + symbol: 'USDC', + }, + }, + outstandingInvestOrders: new Currency(0n, 6), + outstandingRedeemOrders: new Currency(0n, 6), + fulfilledInvestOrders: new Currency(0n, 6), + fulfilledRedeemOrders: new Currency(0n, 6), + yield7DaysAnnualized: null, + yield30DaysAnnualized: null, + yield90DaysAnnualized: null, + yieldSinceInception: new Perquintill(0n), + yieldMTD: null, + yieldQTD: null, + yieldYTD: null, + yieldSinceLastPeriod: null, + }, + { + id: 'tranche-2', + price: Price.fromFloat('1.12'), + timestamp: '2024-01-02T00:00:00Z', + trancheId: 'junior', + poolId: 'pool-1', + tokenSupply: new Currency(0n, 6), + pool: { + currency: { + decimals: 6, + symbol: 'USDC', + }, + }, + outstandingInvestOrders: new Currency(0n, 6), + outstandingRedeemOrders: new Currency(0n, 6), + fulfilledInvestOrders: new Currency(0n, 6), + fulfilledRedeemOrders: new Currency(0n, 6), + yield7DaysAnnualized: null, + yield30DaysAnnualized: null, + yield90DaysAnnualized: null, + yieldSinceInception: new Perquintill(0n), + yieldMTD: null, + yieldQTD: null, + yieldYTD: null, + yieldSinceLastPeriod: null, + }, + ], +} diff --git a/src/types/poolMetadata.ts b/src/types/poolMetadata.ts new file mode 100644 index 0000000..a47d33f --- /dev/null +++ b/src/types/poolMetadata.ts @@ -0,0 +1,83 @@ +export type FileType = { uri: string; mime: string } + +export type PoolMetadata = { + version?: number + pool: { + name: string + icon: FileType | null + asset: { + class: 'Public credit' | 'Private credit' + subClass: string + } + investorType: string + poolStructure: string + poolFees?: { + id: number + name: string + feePosition: 'Top of waterfall' + feeType?: string + }[] + newInvestmentsStatus?: Record + issuer: { + repName: string + name: string + description: string + email: string + logo?: FileType | null + shortDescription: string + categories: { type: string; value: string; customType?: string }[] + } + links: { + executiveSummary: FileType | null + forum?: string + website?: string + } + details?: { + title: string + body: string + }[] + status: 'open' | 'upcoming' | 'hidden' + listed: boolean + reports?: { + author: { + name: string + title: string + avatar: FileType | null + } + uri: string + }[] + poolRatings?: { + agency?: string + value?: string + reportUrl?: string + reportFile?: FileType | null + }[] + } + pod?: { + indexer?: string | null + } + tranches: Record< + string, + { + icon?: FileType | null + minInitialInvestment?: string + targetAPY?: string // only junior tranche (index: 0) has targetAPY + } + > + loanTemplates?: { + id: string + createdAt: string + }[] + adminMultisig?: { + signers: string[] + threshold: number + } + onboarding?: { + kybRestrictedCountries?: string[] + kycRestrictedCountries?: string[] + externalOnboardingUrl?: string + tranches: { [trancheId: string]: { agreement: FileType | undefined; openForOnboarding: boolean } } + podReadAccess?: boolean + taxInfoRequired?: boolean + } +} diff --git a/src/utils/BigInt.ts b/src/utils/BigInt.ts index 371ef33..e2f2908 100644 --- a/src/utils/BigInt.ts +++ b/src/utils/BigInt.ts @@ -36,7 +36,7 @@ export abstract class BigIntWrapper { } export class DecimalWrapper extends BigIntWrapper { - protected decimals: number + readonly decimals: number constructor(value: Numeric | bigint, decimals: number) { super(value) @@ -45,7 +45,7 @@ export class DecimalWrapper extends BigIntWrapper { static _fromFloat(num: Numeric, decimals: number) { const n = Dec(num.toString()).mul(Dec(10).pow(decimals)).toDecimalPlaces(0).toString() - if (Dec(n).lt(1)) { + if (Dec(n).gt(0) && Dec(n).lt(1)) { throw new Error(`${num} is too small to be represented with ${decimals} decimals`) } return new (this as any)(n, decimals) as T @@ -129,6 +129,8 @@ export class Currency extends DecimalWrapper { return Currency._fromFloat(num, decimals) } + static ZERO = new Currency(0n, 0) + add(value: bigint | Currency) { return this._add(value) } @@ -152,6 +154,8 @@ export class Token extends Currency { return new Token(n, decimals) } + static override ZERO = new Token(0n, 0) + override add(value: bigint | Token) { return this._add(value) } diff --git a/src/utils/date.ts b/src/utils/date.ts index 70af8f7..933c454 100644 --- a/src/utils/date.ts +++ b/src/utils/date.ts @@ -19,17 +19,36 @@ export function getPeriod(date: Date, groupBy: GroupBy): string | undefined { } } -export function groupByPeriod(data: T[], groupBy: GroupBy): T[] { - const grouped = new Map() +/** + * Group data by period and return the latest item or all items in the period + * @param data - Data to group + * @param groupBy - Period to group by + * @param strategy - 'latest' returns the latest item in the period, 'all' returns all items in the period + * @returns Grouped data + */ +export function groupByPeriod(data: T[], groupBy: GroupBy, strategy: 'all'): T[][] +export function groupByPeriod(data: T[], groupBy: GroupBy, strategy?: 'latest'): T[] +export function groupByPeriod( + data: T[], + groupBy: GroupBy, + strategy: 'latest' | 'all' = 'latest' +): T[] | T[][] { + const grouped = new Map() data.forEach((item) => { - const period = getPeriod(new Date(item.date), groupBy) + const period = getPeriod(new Date(item.timestamp), groupBy) if (!period) return - if (!grouped.has(period) || new Date(item.date) > new Date(grouped.get(period)!.date)) { - grouped.set(period, item) + if (!grouped.has(period)) { + grouped.set(period, []) } + grouped.get(period)!.push(item) + + // Sort by timestamp within each group + grouped.get(period)!.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()) }) - return Array.from(grouped.values()) + return strategy === 'latest' + ? Array.from(grouped.values()).map((group) => group[group.length - 1] as T) + : Array.from(grouped.values()) } diff --git a/yarn.lock b/yarn.lock index c82913d..f1e8613 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,13 +5,20 @@ __metadata: version: 8 cacheKey: 10c0 -"@adraffy/ens-normalize@npm:1.11.0": +"@adraffy/ens-normalize@npm:^1.10.1": version: 1.11.0 resolution: "@adraffy/ens-normalize@npm:1.11.0" checksum: 10c0/5111d0f1a273468cb5661ed3cf46ee58de8f32f84e2ebc2365652e66c1ead82649df94c736804e2b9cfa831d30ef24e1cc3575d970dbda583416d3a98d8870a6 languageName: node linkType: hard +"@bcoe/v8-coverage@npm:^0.2.3": + version: 0.2.3 + resolution: "@bcoe/v8-coverage@npm:0.2.3" + checksum: 10c0/6b80ae4cb3db53f486da2dc63b6e190a74c8c3cca16bb2733f234a0b6a9382b09b146488ae08e2b22cf00f6c83e20f3e040a2f7894f05c045c946d6a090b1d52 + languageName: node + linkType: hard + "@centrifuge/sdk@workspace:.": version: 0.0.0-use.local resolution: "@centrifuge/sdk@workspace:." @@ -21,6 +28,7 @@ __metadata: "@types/node": "npm:^22.7.8" "@types/sinon": "npm:^17.0.3" "@types/sinon-chai": "npm:^4" + c8: "npm:^10.1.2" chai: "npm:^5.1.2" decimal.js-light: "npm:^2.5.1" dotenv: "npm:^16.4.5" @@ -34,6 +42,7 @@ __metadata: rxjs: "npm:^7.8.1" sinon: "npm:^19.0.2" sinon-chai: "npm:^4.0.0" + source-map-support: "npm:^0.5.21" ts-node: "npm:^10.9.2" typescript: "npm:~5.6.3" typescript-eslint: "npm:^8.8.1" @@ -53,44 +62,44 @@ __metadata: linkType: hard "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": - version: 4.4.0 - resolution: "@eslint-community/eslint-utils@npm:4.4.0" + version: 4.4.1 + resolution: "@eslint-community/eslint-utils@npm:4.4.1" dependencies: - eslint-visitor-keys: "npm:^3.3.0" + eslint-visitor-keys: "npm:^3.4.3" peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - checksum: 10c0/7e559c4ce59cd3a06b1b5a517b593912e680a7f981ae7affab0d01d709e99cd5647019be8fafa38c350305bc32f1f7d42c7073edde2ab536c745e365f37b607e + checksum: 10c0/2aa0ac2fc50ff3f234408b10900ed4f1a0b19352f21346ad4cc3d83a1271481bdda11097baa45d484dd564c895e0762a27a8240be7a256b3ad47129e96528252 languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.11.0": - version: 4.11.1 - resolution: "@eslint-community/regexpp@npm:4.11.1" - checksum: 10c0/fbcc1cb65ef5ed5b92faa8dc542e035269065e7ebcc0b39c81a4fe98ad35cfff20b3c8df048641de15a7757e07d69f85e2579c1a5055f993413ba18c055654f8 +"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.12.1": + version: 4.12.1 + resolution: "@eslint-community/regexpp@npm:4.12.1" + checksum: 10c0/a03d98c246bcb9109aec2c08e4d10c8d010256538dcb3f56610191607214523d4fb1b00aa81df830b6dffb74c5fa0be03642513a289c567949d3e550ca11cdf6 languageName: node linkType: hard -"@eslint/config-array@npm:^0.18.0": - version: 0.18.0 - resolution: "@eslint/config-array@npm:0.18.0" +"@eslint/config-array@npm:^0.19.0": + version: 0.19.0 + resolution: "@eslint/config-array@npm:0.19.0" dependencies: "@eslint/object-schema": "npm:^2.1.4" debug: "npm:^4.3.1" minimatch: "npm:^3.1.2" - checksum: 10c0/0234aeb3e6b052ad2402a647d0b4f8a6aa71524bafe1adad0b8db1dfe94d7f5f26d67c80f79bb37ac61361a1d4b14bb8fb475efe501de37263cf55eabb79868f + checksum: 10c0/def23c6c67a8f98dc88f1b87e17a5668e5028f5ab9459661aabfe08e08f2acd557474bbaf9ba227be0921ae4db232c62773dbb7739815f8415678eb8f592dbf5 languageName: node linkType: hard -"@eslint/core@npm:^0.7.0": - version: 0.7.0 - resolution: "@eslint/core@npm:0.7.0" - checksum: 10c0/3cdee8bc6cbb96ac6103d3ead42e59830019435839583c9eb352b94ed558bd78e7ffad5286dc710df21ec1e7bd8f52aa6574c62457a4dd0f01f3736fa4a7d87a +"@eslint/core@npm:^0.9.0": + version: 0.9.0 + resolution: "@eslint/core@npm:0.9.0" + checksum: 10c0/6d8e8e0991cef12314c49425d8d2d9394f5fb1a36753ff82df7c03185a4646cb7c8736cf26638a4a714782cedf4b23cfc17667d282d3e5965b3920a0e7ce20d4 languageName: node linkType: hard -"@eslint/eslintrc@npm:^3.1.0": - version: 3.1.0 - resolution: "@eslint/eslintrc@npm:3.1.0" +"@eslint/eslintrc@npm:^3.2.0": + version: 3.2.0 + resolution: "@eslint/eslintrc@npm:3.2.0" dependencies: ajv: "npm:^6.12.4" debug: "npm:^4.3.2" @@ -101,14 +110,14 @@ __metadata: js-yaml: "npm:^4.1.0" minimatch: "npm:^3.1.2" strip-json-comments: "npm:^3.1.1" - checksum: 10c0/5b7332ed781edcfc98caa8dedbbb843abfb9bda2e86538529c843473f580e40c69eb894410eddc6702f487e9ee8f8cfa8df83213d43a8fdb549f23ce06699167 + checksum: 10c0/43867a07ff9884d895d9855edba41acf325ef7664a8df41d957135a81a477ff4df4196f5f74dc3382627e5cc8b7ad6b815c2cea1b58f04a75aced7c43414ab8b languageName: node linkType: hard -"@eslint/js@npm:9.13.0": - version: 9.13.0 - resolution: "@eslint/js@npm:9.13.0" - checksum: 10c0/672257bffe17777b8a98bd80438702904cc7a0b98b9c2e426a8a10929198b3553edf8a3fc20feed4133c02e7c8f7331a0ef1b23e5dab8e4469f7f1791beff1e0 +"@eslint/js@npm:9.15.0": + version: 9.15.0 + resolution: "@eslint/js@npm:9.15.0" + checksum: 10c0/56552966ab1aa95332f70d0e006db5746b511c5f8b5e0c6a9b2d6764ff6d964e0b2622731877cbc4e3f0e74c5b39191290d5f48147be19175292575130d499ab languageName: node linkType: hard @@ -119,29 +128,29 @@ __metadata: languageName: node linkType: hard -"@eslint/plugin-kit@npm:^0.2.0": - version: 0.2.1 - resolution: "@eslint/plugin-kit@npm:0.2.1" +"@eslint/plugin-kit@npm:^0.2.3": + version: 0.2.3 + resolution: "@eslint/plugin-kit@npm:0.2.3" dependencies: levn: "npm:^0.4.1" - checksum: 10c0/34b1ecb35df97b0adeb6a43366fc1b8aa1a54d23fc9753019277e80a7295724fddb547a795fd59c9eb56d690bbf0d76d7f2286cb0f5db367a86a763d5acbde5f + checksum: 10c0/89a8035976bb1780e3fa8ffe682df013bd25f7d102d991cecd3b7c297f4ce8c1a1b6805e76dd16465b5353455b670b545eff2b4ec3133e0eab81a5f9e99bd90f languageName: node linkType: hard -"@humanfs/core@npm:^0.19.0": - version: 0.19.0 - resolution: "@humanfs/core@npm:0.19.0" - checksum: 10c0/f87952d5caba6ae427a620eff783c5d0b6cef0cfc256dec359cdaa636c5f161edb8d8dad576742b3de7f0b2f222b34aad6870248e4b7d2177f013426cbcda232 +"@humanfs/core@npm:^0.19.1": + version: 0.19.1 + resolution: "@humanfs/core@npm:0.19.1" + checksum: 10c0/aa4e0152171c07879b458d0e8a704b8c3a89a8c0541726c6b65b81e84fd8b7564b5d6c633feadc6598307d34564bd53294b533491424e8e313d7ab6c7bc5dc67 languageName: node linkType: hard -"@humanfs/node@npm:^0.16.5": - version: 0.16.5 - resolution: "@humanfs/node@npm:0.16.5" +"@humanfs/node@npm:^0.16.6": + version: 0.16.6 + resolution: "@humanfs/node@npm:0.16.6" dependencies: - "@humanfs/core": "npm:^0.19.0" + "@humanfs/core": "npm:^0.19.1" "@humanwhocodes/retry": "npm:^0.3.0" - checksum: 10c0/41c365ab09e7c9eaeed373d09243195aef616d6745608a36fc3e44506148c28843872f85e69e2bf5f1e992e194286155a1c1cecfcece6a2f43875e37cd243935 + checksum: 10c0/8356359c9f60108ec204cbd249ecd0356667359b2524886b357617c4a7c3b6aace0fd5a369f63747b926a762a88f8a25bc066fa1778508d110195ce7686243e1 languageName: node linkType: hard @@ -152,13 +161,20 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/retry@npm:^0.3.0, @humanwhocodes/retry@npm:^0.3.1": +"@humanwhocodes/retry@npm:^0.3.0": version: 0.3.1 resolution: "@humanwhocodes/retry@npm:0.3.1" checksum: 10c0/f0da1282dfb45e8120480b9e2e275e2ac9bbe1cf016d046fdad8e27cc1285c45bb9e711681237944445157b430093412b4446c1ab3fc4bb037861b5904101d3b languageName: node linkType: hard +"@humanwhocodes/retry@npm:^0.4.1": + version: 0.4.1 + resolution: "@humanwhocodes/retry@npm:0.4.1" + checksum: 10c0/be7bb6841c4c01d0b767d9bb1ec1c9359ee61421ce8ba66c249d035c5acdfd080f32d55a5c9e859cdd7868788b8935774f65b2caf24ec0b7bd7bf333791f063b + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -173,14 +189,21 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:^3.0.3": +"@istanbuljs/schema@npm:^0.1.2, @istanbuljs/schema@npm:^0.1.3": + version: 0.1.3 + resolution: "@istanbuljs/schema@npm:0.1.3" + checksum: 10c0/61c5286771676c9ca3eb2bd8a7310a9c063fb6e0e9712225c8471c582d157392c88f5353581c8c9adbe0dff98892317d2fdfc56c3499aa42e0194405206a963a + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": version: 3.1.2 resolution: "@jridgewell/resolve-uri@npm:3.1.2" checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.10": +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14": version: 1.5.0 resolution: "@jridgewell/sourcemap-codec@npm:1.5.0" checksum: 10c0/2eb864f276eb1096c3c11da3e9bb518f6d9fc0023c78344cdc037abadc725172c70314bdb360f2d4b7bffec7f5d657ce006816bc5d4ecb35e61b66132db00c18 @@ -197,7 +220,17 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:1.6.0, @noble/curves@npm:^1.4.0, @noble/curves@npm:~1.6.0": +"@jridgewell/trace-mapping@npm:^0.3.12": + version: 0.3.25 + resolution: "@jridgewell/trace-mapping@npm:0.3.25" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 10c0/3d1ce6ebc69df9682a5a8896b414c6537e428a1d68b02fcc8363b04284a8ca0df04d0ee3013132252ab14f2527bc13bea6526a912ecb5658f0e39fd2860b4df4 + languageName: node + linkType: hard + +"@noble/curves@npm:1.6.0, @noble/curves@npm:~1.6.0": version: 1.6.0 resolution: "@noble/curves@npm:1.6.0" dependencies: @@ -206,13 +239,36 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.5.0, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:~1.5.0": +"@noble/curves@npm:^1.4.0, @noble/curves@npm:^1.6.0, @noble/curves@npm:~1.7.0": + version: 1.7.0 + resolution: "@noble/curves@npm:1.7.0" + dependencies: + "@noble/hashes": "npm:1.6.0" + checksum: 10c0/3317ec9b7699d2476707a89ceb3ddce60e69bac287561a31dd533669408633e093860fea5067eb9c54e5a7ced0705da1cba8859b6b1e0c48d3afff55fe2e77d0 + languageName: node + linkType: hard + +"@noble/hashes@npm:1.5.0, @noble/hashes@npm:~1.5.0": version: 1.5.0 resolution: "@noble/hashes@npm:1.5.0" checksum: 10c0/1b46539695fbfe4477c0822d90c881a04d4fa2921c08c552375b444a48cac9930cb1ee68de0a3c7859e676554d0f3771999716606dc4d8f826e414c11692cdd9 languageName: node linkType: hard +"@noble/hashes@npm:1.6.0": + version: 1.6.0 + resolution: "@noble/hashes@npm:1.6.0" + checksum: 10c0/e7e75898257fb36d933935fcdf1cc67ca7c083eb7b2411aa57fde7eb494c2cea0bec03686462032e25d5b0e1e4ab7357d1afb6718f6a68515db1f392141e9f14 + languageName: node + linkType: hard + +"@noble/hashes@npm:^1.4.0, @noble/hashes@npm:^1.5.0, @noble/hashes@npm:~1.6.0": + version: 1.6.1 + resolution: "@noble/hashes@npm:1.6.1" + checksum: 10c0/27643cd8b551bc933b57cc29aa8c8763d586552fc4c3e06ecf7897f55be3463c0c9dff7f6ebacd88e5ce6d0cdb5415ca4874d0cf4359b5ea4a85be21ada03aab + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -276,6 +332,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:~1.2.1": + version: 1.2.1 + resolution: "@scure/base@npm:1.2.1" + checksum: 10c0/e61068854370855b89c50c28fa4092ea6780f1e0db64ea94075ab574ebcc964f719a3120dc708db324991f4b3e652d92ebda03fce2bf6a4900ceeacf9c0ff933 + languageName: node + linkType: hard + "@scure/bip32@npm:1.5.0": version: 1.5.0 resolution: "@scure/bip32@npm:1.5.0" @@ -287,6 +350,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:^1.5.0": + version: 1.6.0 + resolution: "@scure/bip32@npm:1.6.0" + dependencies: + "@noble/curves": "npm:~1.7.0" + "@noble/hashes": "npm:~1.6.0" + "@scure/base": "npm:~1.2.1" + checksum: 10c0/5a5eff8c0bc0b53d70528c5eda6efa7ed6d186a5c9ba0a339edf9c150ee3f331d837ffe29d2c6c6336b1f88ad90aa8b6e596a4950217343f36916d8024f79bdf + languageName: node + linkType: hard + "@scure/bip39@npm:1.4.0": version: 1.4.0 resolution: "@scure/bip39@npm:1.4.0" @@ -297,6 +371,16 @@ __metadata: languageName: node linkType: hard +"@scure/bip39@npm:^1.4.0": + version: 1.5.0 + resolution: "@scure/bip39@npm:1.5.0" + dependencies: + "@noble/hashes": "npm:~1.6.0" + "@scure/base": "npm:~1.2.1" + checksum: 10c0/114ab88fb00269d17a73d5c39a2cade47403e05f6df5a8d6f5da6e7f2b071966fe8f656a740dc3399acd006163f234e82b680544c38004703dbb60f8a29daf73 + languageName: node + linkType: hard + "@sinonjs/commons@npm:^3.0.1": version: 3.0.1 resolution: "@sinonjs/commons@npm:3.0.1" @@ -361,7 +445,7 @@ __metadata: languageName: node linkType: hard -"@types/chai@npm:*": +"@types/chai@npm:*, @types/chai@npm:^5.0.0": version: 5.0.1 resolution: "@types/chai@npm:5.0.1" dependencies: @@ -370,13 +454,6 @@ __metadata: languageName: node linkType: hard -"@types/chai@npm:^5.0.0": - version: 5.0.0 - resolution: "@types/chai@npm:5.0.0" - checksum: 10c0/fcce55f2bbb8485fc860a1dcbac17c1a685b598cfc91a55d37b65b1642b921cf736caa8cce9dcc530830d900f78ab95cf43db4e118db34a5176f252cacd9e1e8 - languageName: node - linkType: hard - "@types/deep-eql@npm:*": version: 4.0.2 resolution: "@types/deep-eql@npm:4.0.2" @@ -391,6 +468,13 @@ __metadata: languageName: node linkType: hard +"@types/istanbul-lib-coverage@npm:^2.0.1": + version: 2.0.6 + resolution: "@types/istanbul-lib-coverage@npm:2.0.6" + checksum: 10c0/3948088654f3eeb45363f1db158354fb013b362dba2a5c2c18c559484d5eb9f6fd85b23d66c0a7c2fcfab7308d0a585b14dadaca6cc8bf89ebfdc7f8f5102fb7 + languageName: node + linkType: hard + "@types/json-schema@npm:^7.0.15": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" @@ -399,18 +483,18 @@ __metadata: linkType: hard "@types/mocha@npm:^10.0.9": - version: 10.0.9 - resolution: "@types/mocha@npm:10.0.9" - checksum: 10c0/76dd782ac7e971ea159d4a7fd40c929afa051e040be3f41187ff03a2d7b3279e19828ddaa498ba1757b3e6b91316263bb7640db0e906938275b97a06e087b989 + version: 10.0.10 + resolution: "@types/mocha@npm:10.0.10" + checksum: 10c0/d2b8c48138cde6923493e42b38e839695eb42edd04629abe480a8f34c0e3f50dd82a55832c2e8d2b6e6f9e4deb492d7d733e600fbbdd5a0ceccbcfc6844ff9d5 languageName: node linkType: hard "@types/node@npm:^22.7.8": - version: 22.7.8 - resolution: "@types/node@npm:22.7.8" + version: 22.10.0 + resolution: "@types/node@npm:22.10.0" dependencies: - undici-types: "npm:~6.19.2" - checksum: 10c0/3d3b3a2ec5a57ca4fd37b34dce415620993ca5f87cea2c728ffe73aa31446dbfe19c53171c478447bd7d78011ef4845a46ab2f0dc38e699cc75b3d100a60c690 + undici-types: "npm:~6.20.0" + checksum: 10c0/efb3783b6fe74b4300c5bdd4f245f1025887d9b1d0950edae584af58a30d95cc058c10b4b3428f8300e4318468b605240c2ede8fcfb6ead2e0f05bca31e54c1b languageName: node linkType: hard @@ -440,15 +524,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:8.11.0": - version: 8.11.0 - resolution: "@typescript-eslint/eslint-plugin@npm:8.11.0" +"@typescript-eslint/eslint-plugin@npm:8.16.0": + version: 8.16.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.16.0" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:8.11.0" - "@typescript-eslint/type-utils": "npm:8.11.0" - "@typescript-eslint/utils": "npm:8.11.0" - "@typescript-eslint/visitor-keys": "npm:8.11.0" + "@typescript-eslint/scope-manager": "npm:8.16.0" + "@typescript-eslint/type-utils": "npm:8.16.0" + "@typescript-eslint/utils": "npm:8.16.0" + "@typescript-eslint/visitor-keys": "npm:8.16.0" graphemer: "npm:^1.4.0" ignore: "npm:^5.3.1" natural-compare: "npm:^1.4.0" @@ -459,66 +543,68 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/be509f7bb0c0c596801059b06995a81a1c326cc6ac31d96a32f7b6b7d7b495f9bad4dc442aa6e923d22515e62c668d3c14695c68bd6e0be1d4bf72158b7fd2d6 + checksum: 10c0/b03612b726ee5aff631cd50e05ceeb06a522e64465e4efdc134e3a27a09406b959ef7a05ec4acef1956b3674dc4fedb6d3a62ce69382f9e30c227bd4093003e5 languageName: node linkType: hard -"@typescript-eslint/parser@npm:8.11.0": - version: 8.11.0 - resolution: "@typescript-eslint/parser@npm:8.11.0" +"@typescript-eslint/parser@npm:8.16.0": + version: 8.16.0 + resolution: "@typescript-eslint/parser@npm:8.16.0" dependencies: - "@typescript-eslint/scope-manager": "npm:8.11.0" - "@typescript-eslint/types": "npm:8.11.0" - "@typescript-eslint/typescript-estree": "npm:8.11.0" - "@typescript-eslint/visitor-keys": "npm:8.11.0" + "@typescript-eslint/scope-manager": "npm:8.16.0" + "@typescript-eslint/types": "npm:8.16.0" + "@typescript-eslint/typescript-estree": "npm:8.16.0" + "@typescript-eslint/visitor-keys": "npm:8.16.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.57.0 || ^9.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 10c0/e83f239fec60697083e5dcb1c8948340e783ea6e043fe9a65d557faef8882963b09d69aacd736eb8ab18a768769a7bbfc3de0f1251d4bba080613541acb0741c + checksum: 10c0/e49c6640a7a863a16baecfbc5b99392a4731e9c7e9c9aaae4efbc354e305485fe0f39a28bf0acfae85bc01ce37fe0cc140fd315fdaca8b18f9b5e0addff8ceae languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.11.0": - version: 8.11.0 - resolution: "@typescript-eslint/scope-manager@npm:8.11.0" +"@typescript-eslint/scope-manager@npm:8.16.0": + version: 8.16.0 + resolution: "@typescript-eslint/scope-manager@npm:8.16.0" dependencies: - "@typescript-eslint/types": "npm:8.11.0" - "@typescript-eslint/visitor-keys": "npm:8.11.0" - checksum: 10c0/0910da62d8ae261711dd9f89d5c7d8e96ff13c50054436256e5a661309229cb49e3b8189c9468d36b6c4d3f7cddd121519ea78f9b18c9b869a808834b079b2ea + "@typescript-eslint/types": "npm:8.16.0" + "@typescript-eslint/visitor-keys": "npm:8.16.0" + checksum: 10c0/23b7c738b83f381c6419a36e6ca951944187e3e00abb8e012bce8041880410fe498303e28bdeb0e619023a69b14cf32a5ec1f9427c5382807788cd8e52a46a6e languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.11.0": - version: 8.11.0 - resolution: "@typescript-eslint/type-utils@npm:8.11.0" +"@typescript-eslint/type-utils@npm:8.16.0": + version: 8.16.0 + resolution: "@typescript-eslint/type-utils@npm:8.16.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:8.11.0" - "@typescript-eslint/utils": "npm:8.11.0" + "@typescript-eslint/typescript-estree": "npm:8.16.0" + "@typescript-eslint/utils": "npm:8.16.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.3.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 10c0/b69e31c1599ceeb20c29052a4ddb33a554174a3a4c55ee37d90c9b8250af6ef978a0b9ddbeefef4e83d62c4caea1bfa2d8088527f397bde69fb4ab9b360d794a + checksum: 10c0/24c0e815c8bdf99bf488c7528bd6a7c790e8b3b674cb7fb075663afc2ee26b48e6f4cf7c0d14bb21e2376ca62bd8525cbcb5688f36135b00b62b1d353d7235b9 languageName: node linkType: hard -"@typescript-eslint/types@npm:8.11.0": - version: 8.11.0 - resolution: "@typescript-eslint/types@npm:8.11.0" - checksum: 10c0/5ccdd3eeee077a6fc8e7f4bc0e0cbc9327b1205a845253ec5c0c6c49ff915e853161df00c24a0ffb4b8ec745d3f153dd0e066400a021c844c026e31121f46699 +"@typescript-eslint/types@npm:8.16.0": + version: 8.16.0 + resolution: "@typescript-eslint/types@npm:8.16.0" + checksum: 10c0/141e257ab4060a9c0e2e14334ca14ab6be713659bfa38acd13be70a699fb5f36932a2584376b063063ab3d723b24bc703dbfb1ce57d61d7cfd7ec5bd8a975129 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.11.0": - version: 8.11.0 - resolution: "@typescript-eslint/typescript-estree@npm:8.11.0" +"@typescript-eslint/typescript-estree@npm:8.16.0": + version: 8.16.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.16.0" dependencies: - "@typescript-eslint/types": "npm:8.11.0" - "@typescript-eslint/visitor-keys": "npm:8.11.0" + "@typescript-eslint/types": "npm:8.16.0" + "@typescript-eslint/visitor-keys": "npm:8.16.0" debug: "npm:^4.3.4" fast-glob: "npm:^3.3.2" is-glob: "npm:^4.0.3" @@ -528,31 +614,34 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/b629ad3cd32b005d5c1d67c36958a418f8672efebea869399834f4f201ebf90b942165eebb5c9d9799dcabdc2cc26e5fabb00629f76b158847f42e1a491a75a6 + checksum: 10c0/f28fea5af4798a718b6735d1758b791a331af17386b83cb2856d89934a5d1693f7cb805e73c3b33f29140884ac8ead9931b1d7c3de10176fa18ca7a346fe10d0 languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.11.0": - version: 8.11.0 - resolution: "@typescript-eslint/utils@npm:8.11.0" +"@typescript-eslint/utils@npm:8.16.0": + version: 8.16.0 + resolution: "@typescript-eslint/utils@npm:8.16.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" - "@typescript-eslint/scope-manager": "npm:8.11.0" - "@typescript-eslint/types": "npm:8.11.0" - "@typescript-eslint/typescript-estree": "npm:8.11.0" + "@typescript-eslint/scope-manager": "npm:8.16.0" + "@typescript-eslint/types": "npm:8.16.0" + "@typescript-eslint/typescript-estree": "npm:8.16.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 - checksum: 10c0/bb5bcc8d928a55b22298e76f834ea6a9fe125a9ffeb6ac23bee0258b3ed32f41e281888a3d0be226a05e1011bb3b70e42a71a40366acdefea6779131c46bc522 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/1e61187eef3da1ab1486d2a977d8f3b1cb8ef7fa26338500a17eb875ca42a8942ef3f2241f509eef74cf7b5620c109483afc7d83d5b0ab79b1e15920f5a49818 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.11.0": - version: 8.11.0 - resolution: "@typescript-eslint/visitor-keys@npm:8.11.0" +"@typescript-eslint/visitor-keys@npm:8.16.0": + version: 8.16.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.16.0" dependencies: - "@typescript-eslint/types": "npm:8.11.0" - eslint-visitor-keys: "npm:^3.4.3" - checksum: 10c0/7a5a49609fdc47e114fe59eee56393c90b122ec8e9520f90b0c5e189635ae1ccfa8e00108f641342c2c8f4637fe9d40c77927cf7c8248a3a660812cb4b7d0c08 + "@typescript-eslint/types": "npm:8.16.0" + eslint-visitor-keys: "npm:^4.2.0" + checksum: 10c0/537df37801831aa8d91082b2adbffafd40305ed4518f0e7d3cbb17cc466d8b9ac95ac91fa232e7fe585d7c522d1564489ec80052ebb2a6ab9bbf89ef9dd9b7bc languageName: node linkType: hard @@ -563,7 +652,7 @@ __metadata: languageName: node linkType: hard -"abitype@npm:1.0.6": +"abitype@npm:1.0.6, abitype@npm:^1.0.6": version: 1.0.6 resolution: "abitype@npm:1.0.6" peerDependencies: @@ -596,12 +685,12 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.11.0, acorn@npm:^8.12.0, acorn@npm:^8.4.1": - version: 8.13.0 - resolution: "acorn@npm:8.13.0" +"acorn@npm:^8.11.0, acorn@npm:^8.14.0, acorn@npm:^8.4.1": + version: 8.14.0 + resolution: "acorn@npm:8.14.0" bin: acorn: bin/acorn - checksum: 10c0/f35dd53d68177c90699f4c37d0bb205b8abe036d955d0eb011ddb7f14a81e6fd0f18893731c457c1b5bd96754683f4c3d80d9a5585ddecaa53cdf84e0b3d68f7 + checksum: 10c0/6d4ee461a7734b2f48836ee0fbb752903606e576cc100eb49340295129ca0b452f3ba91ddd4424a1d4406a98adfb2ebb6bd0ff4c49d7a0930c10e462719bbfd7 languageName: node linkType: hard @@ -797,6 +886,39 @@ __metadata: languageName: node linkType: hard +"buffer-from@npm:^1.0.0": + version: 1.1.2 + resolution: "buffer-from@npm:1.1.2" + checksum: 10c0/124fff9d66d691a86d3b062eff4663fe437a9d9ee4b47b1b9e97f5a5d14f6d5399345db80f796827be7c95e70a8e765dd404b7c3ff3b3324f98e9b0c8826cc34 + languageName: node + linkType: hard + +"c8@npm:^10.1.2": + version: 10.1.2 + resolution: "c8@npm:10.1.2" + dependencies: + "@bcoe/v8-coverage": "npm:^0.2.3" + "@istanbuljs/schema": "npm:^0.1.3" + find-up: "npm:^5.0.0" + foreground-child: "npm:^3.1.1" + istanbul-lib-coverage: "npm:^3.2.0" + istanbul-lib-report: "npm:^3.0.1" + istanbul-reports: "npm:^3.1.6" + test-exclude: "npm:^7.0.1" + v8-to-istanbul: "npm:^9.0.0" + yargs: "npm:^17.7.2" + yargs-parser: "npm:^21.1.1" + peerDependencies: + monocart-coverage-reports: ^2 + peerDependenciesMeta: + monocart-coverage-reports: + optional: true + bin: + c8: bin/c8.js + checksum: 10c0/882903f22c08f9053b7b274ba31c374cf141d027c46cda57e6472798f82437c5d73fe25bd25b60d6b01c9de383615ae932e6c4d7d4acd7ea231216215f207217 + languageName: node + linkType: hard + "cacache@npm:^18.0.0": version: 18.0.4 resolution: "cacache@npm:18.0.4" @@ -929,6 +1051,17 @@ __metadata: languageName: node linkType: hard +"cliui@npm:^8.0.1": + version: 8.0.1 + resolution: "cliui@npm:8.0.1" + dependencies: + string-width: "npm:^4.2.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^7.0.0" + checksum: 10c0/4bda0f09c340cbb6dfdc1ed508b3ca080f12992c18d68c6be4d9cf51756033d5266e61ec57529e610dacbf4da1c634423b0c1b11037709cc6b09045cbd815df5 + languageName: node + linkType: hard + "color-convert@npm:^1.9.0": version: 1.9.3 resolution: "color-convert@npm:1.9.3" @@ -968,6 +1101,13 @@ __metadata: languageName: node linkType: hard +"convert-source-map@npm:^2.0.0": + version: 2.0.0 + resolution: "convert-source-map@npm:2.0.0" + checksum: 10c0/8f2f7a27a1a011cc6cc88cc4da2d7d0cfa5ee0369508baae3d98c260bb3ac520691464e5bbe4ae7cdf09860c1d69ecc6f70c63c6e7c7f7e3f18ec08484dc7d9b + languageName: node + linkType: hard + "create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" @@ -976,26 +1116,26 @@ __metadata: linkType: hard "cross-spawn@npm:^6.0.5": - version: 6.0.5 - resolution: "cross-spawn@npm:6.0.5" + version: 6.0.6 + resolution: "cross-spawn@npm:6.0.6" dependencies: nice-try: "npm:^1.0.4" path-key: "npm:^2.0.1" semver: "npm:^5.5.0" shebang-command: "npm:^1.2.0" which: "npm:^1.2.9" - checksum: 10c0/e05544722e9d7189b4292c66e42b7abeb21db0d07c91b785f4ae5fefceb1f89e626da2703744657b287e86dcd4af57b54567cef75159957ff7a8a761d9055012 + checksum: 10c0/bf61fb890e8635102ea9bce050515cf915ff6a50ccaa0b37a17dc82fded0fb3ed7af5478b9367b86baee19127ad86af4be51d209f64fd6638c0862dca185fe1d languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2": - version: 7.0.3 - resolution: "cross-spawn@npm:7.0.3" +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.5": + version: 7.0.6 + resolution: "cross-spawn@npm:7.0.6" dependencies: path-key: "npm:^3.1.0" shebang-command: "npm:^2.0.0" which: "npm:^2.0.1" - checksum: 10c0/5738c312387081c98d69c98e105b6327b069197f864a60593245d64c8089c8a0a744e16349281210d56835bb9274130d825a78b2ad6853ca13cfbeffc0c31750 + checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1 languageName: node linkType: hard @@ -1175,9 +1315,9 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.0, es-abstract@npm:^1.23.2": - version: 1.23.3 - resolution: "es-abstract@npm:1.23.3" +"es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.0, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.5": + version: 1.23.5 + resolution: "es-abstract@npm:1.23.5" dependencies: array-buffer-byte-length: "npm:^1.0.1" arraybuffer.prototype.slice: "npm:^1.0.3" @@ -1194,7 +1334,7 @@ __metadata: function.prototype.name: "npm:^1.1.6" get-intrinsic: "npm:^1.2.4" get-symbol-description: "npm:^1.0.2" - globalthis: "npm:^1.0.3" + globalthis: "npm:^1.0.4" gopd: "npm:^1.0.1" has-property-descriptors: "npm:^1.0.2" has-proto: "npm:^1.0.3" @@ -1210,10 +1350,10 @@ __metadata: is-string: "npm:^1.0.7" is-typed-array: "npm:^1.1.13" is-weakref: "npm:^1.0.2" - object-inspect: "npm:^1.13.1" + object-inspect: "npm:^1.13.3" object-keys: "npm:^1.1.1" object.assign: "npm:^4.1.5" - regexp.prototype.flags: "npm:^1.5.2" + regexp.prototype.flags: "npm:^1.5.3" safe-array-concat: "npm:^1.1.2" safe-regex-test: "npm:^1.0.3" string.prototype.trim: "npm:^1.2.9" @@ -1225,7 +1365,7 @@ __metadata: typed-array-length: "npm:^1.0.6" unbox-primitive: "npm:^1.0.2" which-typed-array: "npm:^1.1.15" - checksum: 10c0/d27e9afafb225c6924bee9971a7f25f20c314f2d6cb93a63cada4ac11dcf42040896a6c22e5fb8f2a10767055ed4ddf400be3b1eb12297d281726de470b75666 + checksum: 10c0/1f6f91da9cf7ee2c81652d57d3046621d598654d1d1b05c1578bafe5c4c2d3d69513901679bdca2de589f620666ec21de337e4935cec108a4ed0871d5ef04a5d languageName: node linkType: hard @@ -1266,13 +1406,13 @@ __metadata: linkType: hard "es-to-primitive@npm:^1.2.1": - version: 1.2.1 - resolution: "es-to-primitive@npm:1.2.1" + version: 1.3.0 + resolution: "es-to-primitive@npm:1.3.0" dependencies: - is-callable: "npm:^1.1.4" - is-date-object: "npm:^1.0.1" - is-symbol: "npm:^1.0.2" - checksum: 10c0/0886572b8dc075cb10e50c0af62a03d03a68e1e69c388bd4f10c0649ee41b1fbb24840a1b7e590b393011b5cdbe0144b776da316762653685432df37d6de60f1 + is-callable: "npm:^1.2.7" + is-date-object: "npm:^1.0.5" + is-symbol: "npm:^1.0.4" + checksum: 10c0/c7e87467abb0b438639baa8139f701a06537d2b9bc758f23e8622c3b42fd0fdb5bde0f535686119e446dd9d5e4c0f238af4e14960f4771877cf818d023f6730b languageName: node linkType: hard @@ -1297,54 +1437,54 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:^8.1.0": - version: 8.1.0 - resolution: "eslint-scope@npm:8.1.0" +"eslint-scope@npm:^8.2.0": + version: 8.2.0 + resolution: "eslint-scope@npm:8.2.0" dependencies: esrecurse: "npm:^4.3.0" estraverse: "npm:^5.2.0" - checksum: 10c0/ae1df7accae9ea90465c2ded70f7064d6d1f2962ef4cc87398855c4f0b3a5ab01063e0258d954bb94b184f6759febe04c3118195cab5c51978a7229948ba2875 + checksum: 10c0/8d2d58e2136d548ac7e0099b1a90d9fab56f990d86eb518de1247a7066d38c908be2f3df477a79cf60d70b30ba18735d6c6e70e9914dca2ee515a729975d70d6 languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.3": +"eslint-visitor-keys@npm:^3.4.3": version: 3.4.3 resolution: "eslint-visitor-keys@npm:3.4.3" checksum: 10c0/92708e882c0a5ffd88c23c0b404ac1628cf20104a108c745f240a13c332a11aac54f49a22d5762efbffc18ecbc9a580d1b7ad034bf5f3cc3307e5cbff2ec9820 languageName: node linkType: hard -"eslint-visitor-keys@npm:^4.1.0": - version: 4.1.0 - resolution: "eslint-visitor-keys@npm:4.1.0" - checksum: 10c0/5483ef114c93a136aa234140d7aa3bd259488dae866d35cb0d0b52e6a158f614760a57256ac8d549acc590a87042cb40f6951815caa821e55dc4fd6ef4c722eb +"eslint-visitor-keys@npm:^4.2.0": + version: 4.2.0 + resolution: "eslint-visitor-keys@npm:4.2.0" + checksum: 10c0/2ed81c663b147ca6f578312919483eb040295bbab759e5a371953456c636c5b49a559883e2677112453728d66293c0a4c90ab11cab3428cf02a0236d2e738269 languageName: node linkType: hard "eslint@npm:^9.12.0": - version: 9.13.0 - resolution: "eslint@npm:9.13.0" + version: 9.15.0 + resolution: "eslint@npm:9.15.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" - "@eslint-community/regexpp": "npm:^4.11.0" - "@eslint/config-array": "npm:^0.18.0" - "@eslint/core": "npm:^0.7.0" - "@eslint/eslintrc": "npm:^3.1.0" - "@eslint/js": "npm:9.13.0" - "@eslint/plugin-kit": "npm:^0.2.0" - "@humanfs/node": "npm:^0.16.5" + "@eslint-community/regexpp": "npm:^4.12.1" + "@eslint/config-array": "npm:^0.19.0" + "@eslint/core": "npm:^0.9.0" + "@eslint/eslintrc": "npm:^3.2.0" + "@eslint/js": "npm:9.15.0" + "@eslint/plugin-kit": "npm:^0.2.3" + "@humanfs/node": "npm:^0.16.6" "@humanwhocodes/module-importer": "npm:^1.0.1" - "@humanwhocodes/retry": "npm:^0.3.1" + "@humanwhocodes/retry": "npm:^0.4.1" "@types/estree": "npm:^1.0.6" "@types/json-schema": "npm:^7.0.15" ajv: "npm:^6.12.4" chalk: "npm:^4.0.0" - cross-spawn: "npm:^7.0.2" + cross-spawn: "npm:^7.0.5" debug: "npm:^4.3.2" escape-string-regexp: "npm:^4.0.0" - eslint-scope: "npm:^8.1.0" - eslint-visitor-keys: "npm:^4.1.0" - espree: "npm:^10.2.0" + eslint-scope: "npm:^8.2.0" + eslint-visitor-keys: "npm:^4.2.0" + espree: "npm:^10.3.0" esquery: "npm:^1.5.0" esutils: "npm:^2.0.2" fast-deep-equal: "npm:^3.1.3" @@ -1359,7 +1499,6 @@ __metadata: minimatch: "npm:^3.1.2" natural-compare: "npm:^1.4.0" optionator: "npm:^0.9.3" - text-table: "npm:^0.2.0" peerDependencies: jiti: "*" peerDependenciesMeta: @@ -1367,18 +1506,18 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10c0/d3577444152182a9d8ea8c6a6acb073d3a2773ad73a6b646f432746583ec4bfcd6a44fcc2e37d05d276984e583c46c2d289b3b981ca8f8b4052756a152341d19 + checksum: 10c0/d0d7606f36bfcccb1c3703d0a24df32067b207a616f17efe5fb1765a91d13f085afffc4fc97ecde4ab9c9f4edd64d9b4ce750e13ff7937a25074b24bee15b20f languageName: node linkType: hard -"espree@npm:^10.0.1, espree@npm:^10.2.0": - version: 10.2.0 - resolution: "espree@npm:10.2.0" +"espree@npm:^10.0.1, espree@npm:^10.3.0": + version: 10.3.0 + resolution: "espree@npm:10.3.0" dependencies: - acorn: "npm:^8.12.0" + acorn: "npm:^8.14.0" acorn-jsx: "npm:^5.3.2" - eslint-visitor-keys: "npm:^4.1.0" - checksum: 10c0/2b6bfb683e7e5ab2e9513949879140898d80a2d9867ea1db6ff5b0256df81722633b60a7523a7c614f05a39aeea159dd09ad2a0e90c0e218732fc016f9086215 + eslint-visitor-keys: "npm:^4.2.0" + checksum: 10c0/272beeaca70d0a1a047d61baff64db04664a33d7cfb5d144f84bc8a5c6194c6c8ebe9cc594093ca53add88baa23e59b01e69e8a0160ab32eac570482e165c462 languageName: node linkType: hard @@ -1423,6 +1562,13 @@ __metadata: languageName: node linkType: hard +"eventemitter3@npm:5.0.1": + version: 5.0.1 + resolution: "eventemitter3@npm:5.0.1" + checksum: 10c0/4ba5c00c506e6c786b4d6262cfbce90ddc14c10d4667e5c83ae993c9de88aa856033994dd2b35b83e8dc1170e224e66a319fa80adc4c32adcd2379bbc75da814 + languageName: node + linkType: hard + "exponential-backoff@npm:^3.1.1": version: 3.1.1 resolution: "exponential-backoff@npm:3.1.1" @@ -1521,9 +1667,9 @@ __metadata: linkType: hard "flatted@npm:^3.2.9": - version: 3.3.1 - resolution: "flatted@npm:3.3.1" - checksum: 10c0/324166b125ee07d4ca9bcf3a5f98d915d5db4f39d711fba640a3178b959919aae1f7cfd8aabcfef5826ed8aa8a2aa14cc85b2d7d18ff638ddf4ae3df39573eaf + version: 3.3.2 + resolution: "flatted@npm:3.3.2" + checksum: 10c0/24cc735e74d593b6c767fe04f2ef369abe15b62f6906158079b9874bdb3ee5ae7110bb75042e70cd3f99d409d766f357caf78d5ecee9780206f5fdc5edbad334 languageName: node linkType: hard @@ -1536,7 +1682,7 @@ __metadata: languageName: node linkType: hard -"foreground-child@npm:^3.1.0": +"foreground-child@npm:^3.1.0, foreground-child@npm:^3.1.1": version: 3.3.0 resolution: "foreground-child@npm:3.3.0" dependencies: @@ -1665,7 +1811,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2, glob@npm:^10.3.10": +"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.4.1": version: 10.4.5 resolution: "glob@npm:10.4.5" dependencies: @@ -1702,13 +1848,13 @@ __metadata: linkType: hard "globals@npm:^15.11.0": - version: 15.11.0 - resolution: "globals@npm:15.11.0" - checksum: 10c0/861e39bb6bd9bd1b9f355c25c962e5eb4b3f0e1567cf60fa6c06e8c502b0ec8706b1cce055d69d84d0b7b8e028bec5418cf629a54e7047e116538d1c1c1a375c + version: 15.12.0 + resolution: "globals@npm:15.12.0" + checksum: 10c0/f34e0a1845b694f45188331742af9f488b07ba7440a06e9d2039fce0386fbbfc24afdbb9846ebdccd4092d03644e43081c49eb27b30f4b88e43af156e1c1dc34 languageName: node linkType: hard -"globalthis@npm:^1.0.3": +"globalthis@npm:^1.0.4": version: 1.0.4 resolution: "globalthis@npm:1.0.4" dependencies: @@ -1819,6 +1965,13 @@ __metadata: languageName: node linkType: hard +"html-escaper@npm:^2.0.0": + version: 2.0.2 + resolution: "html-escaper@npm:2.0.2" + checksum: 10c0/208e8a12de1a6569edbb14544f4567e6ce8ecc30b9394fcaa4e7bb1e60c12a7c9a1ed27e31290817157e8626f3a4f29e76c8747030822eb84a6abb15c255f0a0 + languageName: node + linkType: hard + "http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" @@ -1941,6 +2094,15 @@ __metadata: languageName: node linkType: hard +"is-async-function@npm:^2.0.0": + version: 2.0.0 + resolution: "is-async-function@npm:2.0.0" + dependencies: + has-tostringtag: "npm:^1.0.0" + checksum: 10c0/787bc931576aad525d751fc5ce211960fe91e49ac84a5c22d6ae0bc9541945fbc3f686dc590c3175722ce4f6d7b798a93f6f8ff4847fdb2199aea6f4baf5d668 + languageName: node + linkType: hard + "is-bigint@npm:^1.0.1": version: 1.0.4 resolution: "is-bigint@npm:1.0.4" @@ -1969,7 +2131,7 @@ __metadata: languageName: node linkType: hard -"is-callable@npm:^1.1.3, is-callable@npm:^1.1.4, is-callable@npm:^1.2.7": +"is-callable@npm:^1.1.3, is-callable@npm:^1.2.7": version: 1.2.7 resolution: "is-callable@npm:1.2.7" checksum: 10c0/ceebaeb9d92e8adee604076971dd6000d38d6afc40bb843ea8e45c5579b57671c3f3b50d7f04869618242c6cee08d1b67806a8cb8edaaaf7c0748b3720d6066f @@ -1994,7 +2156,7 @@ __metadata: languageName: node linkType: hard -"is-date-object@npm:^1.0.1": +"is-date-object@npm:^1.0.5": version: 1.0.5 resolution: "is-date-object@npm:1.0.5" dependencies: @@ -2010,6 +2172,15 @@ __metadata: languageName: node linkType: hard +"is-finalizationregistry@npm:^1.1.0": + version: 1.1.0 + resolution: "is-finalizationregistry@npm:1.1.0" + dependencies: + call-bind: "npm:^1.0.7" + checksum: 10c0/1cd94236bfb6e060fe2b973c8726a2782727f7d495b3e8e1d51d3e619c5a3345413706f555956eb5b12af15eba0414118f64a1b19d793ec36b5e6767a13836ac + languageName: node + linkType: hard + "is-fullwidth-code-point@npm:^3.0.0": version: 3.0.0 resolution: "is-fullwidth-code-point@npm:3.0.0" @@ -2017,6 +2188,15 @@ __metadata: languageName: node linkType: hard +"is-generator-function@npm:^1.0.10": + version: 1.0.10 + resolution: "is-generator-function@npm:1.0.10" + dependencies: + has-tostringtag: "npm:^1.0.0" + checksum: 10c0/df03514df01a6098945b5a0cfa1abff715807c8e72f57c49a0686ad54b3b74d394e2d8714e6f709a71eb00c9630d48e73ca1796c1ccc84ac95092c1fecc0d98b + languageName: node + linkType: hard + "is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": version: 4.0.3 resolution: "is-glob@npm:4.0.3" @@ -2033,6 +2213,13 @@ __metadata: languageName: node linkType: hard +"is-map@npm:^2.0.3": + version: 2.0.3 + resolution: "is-map@npm:2.0.3" + checksum: 10c0/2c4d431b74e00fdda7162cd8e4b763d6f6f217edf97d4f8538b94b8702b150610e2c64961340015fe8df5b1fcee33ccd2e9b62619c4a8a3a155f8de6d6d355fc + languageName: node + linkType: hard + "is-negative-zero@npm:^2.0.3": version: 2.0.3 resolution: "is-negative-zero@npm:2.0.3" @@ -2073,6 +2260,13 @@ __metadata: languageName: node linkType: hard +"is-set@npm:^2.0.3": + version: 2.0.3 + resolution: "is-set@npm:2.0.3" + checksum: 10c0/f73732e13f099b2dc879c2a12341cfc22ccaca8dd504e6edae26484bd5707a35d503fba5b4daad530a9b088ced1ae6c9d8200fd92e09b428fe14ea79ce8080b7 + languageName: node + linkType: hard + "is-shared-array-buffer@npm:^1.0.2, is-shared-array-buffer@npm:^1.0.3": version: 1.0.3 resolution: "is-shared-array-buffer@npm:1.0.3" @@ -2091,7 +2285,7 @@ __metadata: languageName: node linkType: hard -"is-symbol@npm:^1.0.2, is-symbol@npm:^1.0.3": +"is-symbol@npm:^1.0.3, is-symbol@npm:^1.0.4": version: 1.0.4 resolution: "is-symbol@npm:1.0.4" dependencies: @@ -2116,6 +2310,13 @@ __metadata: languageName: node linkType: hard +"is-weakmap@npm:^2.0.2": + version: 2.0.2 + resolution: "is-weakmap@npm:2.0.2" + checksum: 10c0/443c35bb86d5e6cc5929cd9c75a4024bb0fff9586ed50b092f94e700b89c43a33b186b76dbc6d54f3d3d09ece689ab38dcdc1af6a482cbe79c0f2da0a17f1299 + languageName: node + linkType: hard + "is-weakref@npm:^1.0.2": version: 1.0.2 resolution: "is-weakref@npm:1.0.2" @@ -2125,6 +2326,16 @@ __metadata: languageName: node linkType: hard +"is-weakset@npm:^2.0.3": + version: 2.0.3 + resolution: "is-weakset@npm:2.0.3" + dependencies: + call-bind: "npm:^1.0.7" + get-intrinsic: "npm:^1.2.4" + checksum: 10c0/8ad6141b6a400e7ce7c7442a13928c676d07b1f315ab77d9912920bf5f4170622f43126f111615788f26c3b1871158a6797c862233124507db0bcc33a9537d1a + languageName: node + linkType: hard + "isarray@npm:^2.0.5": version: 2.0.5 resolution: "isarray@npm:2.0.5" @@ -2164,6 +2375,34 @@ __metadata: languageName: node linkType: hard +"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0": + version: 3.2.2 + resolution: "istanbul-lib-coverage@npm:3.2.2" + checksum: 10c0/6c7ff2106769e5f592ded1fb418f9f73b4411fd5a084387a5410538332b6567cd1763ff6b6cadca9b9eb2c443cce2f7ea7d7f1b8d315f9ce58539793b1e0922b + languageName: node + linkType: hard + +"istanbul-lib-report@npm:^3.0.0, istanbul-lib-report@npm:^3.0.1": + version: 3.0.1 + resolution: "istanbul-lib-report@npm:3.0.1" + dependencies: + istanbul-lib-coverage: "npm:^3.0.0" + make-dir: "npm:^4.0.0" + supports-color: "npm:^7.1.0" + checksum: 10c0/84323afb14392de8b6a5714bd7e9af845cfbd56cfe71ed276cda2f5f1201aea673c7111901227ee33e68e4364e288d73861eb2ed48f6679d1e69a43b6d9b3ba7 + languageName: node + linkType: hard + +"istanbul-reports@npm:^3.1.6": + version: 3.1.7 + resolution: "istanbul-reports@npm:3.1.7" + dependencies: + html-escaper: "npm:^2.0.0" + istanbul-lib-report: "npm:^3.0.0" + checksum: 10c0/a379fadf9cf8dc5dfe25568115721d4a7eb82fbd50b005a6672aff9c6989b20cc9312d7865814e0859cd8df58cbf664482e1d3604be0afde1f7fc3ccc1394a51 + languageName: node + linkType: hard + "jackspeak@npm:^3.1.2": version: 3.4.3 resolution: "jackspeak@npm:3.4.3" @@ -2308,6 +2547,15 @@ __metadata: languageName: node linkType: hard +"make-dir@npm:^4.0.0": + version: 4.0.0 + resolution: "make-dir@npm:4.0.0" + dependencies: + semver: "npm:^7.5.3" + checksum: 10c0/69b98a6c0b8e5c4fe9acb61608a9fbcfca1756d910f51e5dbe7a9e5cfb74fca9b8a0c8a0ffdf1294a740826c1ab4871d5bf3f62f72a3049e5eac6541ddffed68 + languageName: node + linkType: hard + "make-error@npm:^1.1.1": version: 1.3.6 resolution: "make-error@npm:1.3.6" @@ -2480,8 +2728,8 @@ __metadata: linkType: hard "mocha@npm:^10.7.3": - version: 10.7.3 - resolution: "mocha@npm:10.7.3" + version: 10.8.2 + resolution: "mocha@npm:10.8.2" dependencies: ansi-colors: "npm:^4.1.3" browser-stdout: "npm:^1.3.1" @@ -2506,7 +2754,7 @@ __metadata: bin: _mocha: bin/_mocha mocha: bin/mocha.js - checksum: 10c0/76a205905ec626262d903954daca31ba8e0dd4347092f627b98b8508dcdb5b30be62ec8f7a405fab3b2e691bdc099721c3291b330c3ee85b8ec40d3d179f8728 + checksum: 10c0/1f786290a32a1c234f66afe2bfcc68aa50fe9c7356506bd39cca267efb0b4714a63a0cb333815578d63785ba2fba058bf576c2512db73997c0cae0d659a88beb languageName: node linkType: hard @@ -2622,10 +2870,10 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.13.1": - version: 1.13.2 - resolution: "object-inspect@npm:1.13.2" - checksum: 10c0/b97835b4c91ec37b5fd71add84f21c3f1047d1d155d00c0fcd6699516c256d4fcc6ff17a1aced873197fe447f91a3964178fd2a67a1ee2120cdaf60e81a050b4 +"object-inspect@npm:^1.13.1, object-inspect@npm:^1.13.3": + version: 1.13.3 + resolution: "object-inspect@npm:1.13.3" + checksum: 10c0/cc3f15213406be89ffdc54b525e115156086796a515410a8d390215915db9f23c8eab485a06f1297402f440a33715fe8f71a528c1dcbad6e1a3bcaf5a46921d4 languageName: node linkType: hard @@ -2671,6 +2919,26 @@ __metadata: languageName: node linkType: hard +"ox@npm:0.1.2": + version: 0.1.2 + resolution: "ox@npm:0.1.2" + dependencies: + "@adraffy/ens-normalize": "npm:^1.10.1" + "@noble/curves": "npm:^1.6.0" + "@noble/hashes": "npm:^1.5.0" + "@scure/bip32": "npm:^1.5.0" + "@scure/bip39": "npm:^1.4.0" + abitype: "npm:^1.0.6" + eventemitter3: "npm:5.0.1" + peerDependencies: + typescript: ">=5.4.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/9d0615e9a95c316063587fe08dc268476e67429eea897598b2f69cb1509ac66739f888b0b9bc1cfd0b4bd2f1a3fd0af4d3e81d40ba0bf3abd53e36a6f5b21323 + languageName: node + linkType: hard + "p-limit@npm:^3.0.2": version: 3.1.0 resolution: "p-limit@npm:3.1.0" @@ -2823,11 +3091,11 @@ __metadata: linkType: hard "prettier@npm:^3.3.3": - version: 3.3.3 - resolution: "prettier@npm:3.3.3" + version: 3.4.1 + resolution: "prettier@npm:3.4.1" bin: prettier: bin/prettier.cjs - checksum: 10c0/b85828b08e7505716324e4245549b9205c0cacb25342a030ba8885aba2039a115dbcf75a0b7ca3b37bc9d101ee61fab8113fc69ca3359f2a226f1ecc07ad2e26 + checksum: 10c0/2d6cc3101ad9de72b49c59339480b0983e6ff6742143da0c43f476bf3b5ef88ede42ebd9956d7a0a8fa59f7a5990e8ef03c9ad4c37f7e4c9e5db43ee0853156c languageName: node linkType: hard @@ -2891,7 +3159,22 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.5.2": +"reflect.getprototypeof@npm:^1.0.6": + version: 1.0.7 + resolution: "reflect.getprototypeof@npm:1.0.7" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.5" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.4" + gopd: "npm:^1.0.1" + which-builtin-type: "npm:^1.1.4" + checksum: 10c0/841814f7631b55ee42e198cb14a5c25c0752431ab8f0ad9794c32d46ab9fb0d5ba4939edac1f99a174a21443a1400a72bccbbb9ccd9277e4b4bf6d14aabb31c8 + languageName: node + linkType: hard + +"regexp.prototype.flags@npm:^1.5.3": version: 1.5.3 resolution: "regexp.prototype.flags@npm:1.5.3" dependencies: @@ -3021,7 +3304,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.5, semver@npm:^7.6.0": +"semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.6.0": version: 7.6.3 resolution: "semver@npm:7.6.3" bin: @@ -3098,9 +3381,9 @@ __metadata: linkType: hard "shell-quote@npm:^1.6.1": - version: 1.8.1 - resolution: "shell-quote@npm:1.8.1" - checksum: 10c0/8cec6fd827bad74d0a49347057d40dfea1e01f12a6123bf82c4649f3ef152fc2bc6d6176e6376bffcd205d9d0ccb4f1f9acae889384d20baff92186f01ea455a + version: 1.8.2 + resolution: "shell-quote@npm:1.8.2" + checksum: 10c0/85fdd44f2ad76e723d34eb72c753f04d847ab64e9f1f10677e3f518d0e5b0752a176fd805297b30bb8c3a1556ebe6e77d2288dbd7b7b0110c7e941e9e9c20ce1 languageName: node linkType: hard @@ -3175,6 +3458,23 @@ __metadata: languageName: node linkType: hard +"source-map-support@npm:^0.5.21": + version: 0.5.21 + resolution: "source-map-support@npm:0.5.21" + dependencies: + buffer-from: "npm:^1.0.0" + source-map: "npm:^0.6.0" + checksum: 10c0/9ee09942f415e0f721d6daad3917ec1516af746a8120bba7bb56278707a37f1eb8642bde456e98454b8a885023af81a16e646869975f06afc1a711fb90484e7d + languageName: node + linkType: hard + +"source-map@npm:^0.6.0": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 + languageName: node + linkType: hard + "spdx-correct@npm:^3.0.0": version: 3.2.0 resolution: "spdx-correct@npm:3.2.0" @@ -3225,7 +3525,7 @@ __metadata: languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -3373,10 +3673,14 @@ __metadata: languageName: node linkType: hard -"text-table@npm:^0.2.0": - version: 0.2.0 - resolution: "text-table@npm:0.2.0" - checksum: 10c0/02805740c12851ea5982686810702e2f14369a5f4c5c40a836821e3eefc65ffeec3131ba324692a37608294b0fd8c1e55a2dd571ffed4909822787668ddbee5c +"test-exclude@npm:^7.0.1": + version: 7.0.1 + resolution: "test-exclude@npm:7.0.1" + dependencies: + "@istanbuljs/schema": "npm:^0.1.2" + glob: "npm:^10.4.1" + minimatch: "npm:^9.0.4" + checksum: 10c0/6d67b9af4336a2e12b26a68c83308c7863534c65f27ed4ff7068a56f5a58f7ac703e8fc80f698a19bb154fd8f705cdf7ec347d9512b2c522c737269507e7b263 languageName: node linkType: hard @@ -3390,11 +3694,11 @@ __metadata: linkType: hard "ts-api-utils@npm:^1.3.0": - version: 1.3.0 - resolution: "ts-api-utils@npm:1.3.0" + version: 1.4.2 + resolution: "ts-api-utils@npm:1.4.2" peerDependencies: typescript: ">=4.2.0" - checksum: 10c0/f54a0ba9ed56ce66baea90a3fa087a484002e807f28a8ccb2d070c75e76bde64bd0f6dce98b3802834156306050871b67eec325cb4e918015a360a3f0868c77c + checksum: 10c0/b9d82922af42cefa14650397f5ff42a1ff8c8a1b4fac3590fa3e2daeeb3666fbe260a324f55dc748d9653dce30c2a21a148fba928511b2022bedda66423695bf languageName: node linkType: hard @@ -3437,9 +3741,9 @@ __metadata: linkType: hard "tslib@npm:^2.1.0": - version: 2.8.0 - resolution: "tslib@npm:2.8.0" - checksum: 10c0/31e4d14dc1355e9b89e4d3c893a18abb7f90b6886b089c2da91224d0a7752c79f3ddc41bc1aa0a588ac895bd97bb99c5bc2bfdb2f86de849f31caeb3ba79bbe5 + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 languageName: node linkType: hard @@ -3491,8 +3795,8 @@ __metadata: linkType: hard "typed-array-byte-offset@npm:^1.0.2": - version: 1.0.2 - resolution: "typed-array-byte-offset@npm:1.0.2" + version: 1.0.3 + resolution: "typed-array-byte-offset@npm:1.0.3" dependencies: available-typed-arrays: "npm:^1.0.7" call-bind: "npm:^1.0.7" @@ -3500,35 +3804,38 @@ __metadata: gopd: "npm:^1.0.1" has-proto: "npm:^1.0.3" is-typed-array: "npm:^1.1.13" - checksum: 10c0/d2628bc739732072e39269389a758025f75339de2ed40c4f91357023c5512d237f255b633e3106c461ced41907c1bf9a533c7e8578066b0163690ca8bc61b22f + reflect.getprototypeof: "npm:^1.0.6" + checksum: 10c0/5da29585f96671c0521475226d3227000b3e01d1e99208b66bb05b75c7c8f4d0e9cc2e79920f3bfbc792a00102df1daa2608a2753e3f291b671d5a80245bde5b languageName: node linkType: hard "typed-array-length@npm:^1.0.6": - version: 1.0.6 - resolution: "typed-array-length@npm:1.0.6" + version: 1.0.7 + resolution: "typed-array-length@npm:1.0.7" dependencies: call-bind: "npm:^1.0.7" for-each: "npm:^0.3.3" gopd: "npm:^1.0.1" - has-proto: "npm:^1.0.3" is-typed-array: "npm:^1.1.13" possible-typed-array-names: "npm:^1.0.0" - checksum: 10c0/74253d7dc488eb28b6b2711cf31f5a9dcefc9c41b0681fd1c178ed0a1681b4468581a3626d39cd4df7aee3d3927ab62be06aa9ca74e5baf81827f61641445b77 + reflect.getprototypeof: "npm:^1.0.6" + checksum: 10c0/e38f2ae3779584c138a2d8adfa8ecf749f494af3cd3cdafe4e688ce51418c7d2c5c88df1bd6be2bbea099c3f7cea58c02ca02ed438119e91f162a9de23f61295 languageName: node linkType: hard "typescript-eslint@npm:^8.8.1": - version: 8.11.0 - resolution: "typescript-eslint@npm:8.11.0" + version: 8.16.0 + resolution: "typescript-eslint@npm:8.16.0" dependencies: - "@typescript-eslint/eslint-plugin": "npm:8.11.0" - "@typescript-eslint/parser": "npm:8.11.0" - "@typescript-eslint/utils": "npm:8.11.0" + "@typescript-eslint/eslint-plugin": "npm:8.16.0" + "@typescript-eslint/parser": "npm:8.16.0" + "@typescript-eslint/utils": "npm:8.16.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 10c0/8f9b5916c9f47b0cbb26f142d1a266a6aaf33998ec87621252dffb56d8fe0ad01a944f8d8d837e4e6058153a1deee3557527d14fa7bf7ef80a927334529db6bd + checksum: 10c0/3da9401d6c2416b9d95c96a41a9423a5379d233a120cd3304e2c03f191d350ce91cf0c7e60017f7b10c93b4cc1190592702735735b771c1ce1bf68f71a9f1647 languageName: node linkType: hard @@ -3564,10 +3871,10 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~6.19.2": - version: 6.19.8 - resolution: "undici-types@npm:6.19.8" - checksum: 10c0/078afa5990fba110f6824823ace86073b4638f1d5112ee26e790155f481f2a868cc3e0615505b6f4282bdf74a3d8caad715fd809e870c2bb0704e3ea6082f344 +"undici-types@npm:~6.20.0": + version: 6.20.0 + resolution: "undici-types@npm:6.20.0" + checksum: 10c0/68e659a98898d6a836a9a59e6adf14a5d799707f5ea629433e025ac90d239f75e408e2e5ff086afc3cace26f8b26ee52155293564593fbb4a2f666af57fc59bf languageName: node linkType: hard @@ -3612,6 +3919,17 @@ __metadata: languageName: node linkType: hard +"v8-to-istanbul@npm:^9.0.0": + version: 9.3.0 + resolution: "v8-to-istanbul@npm:9.3.0" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.12" + "@types/istanbul-lib-coverage": "npm:^2.0.1" + convert-source-map: "npm:^2.0.0" + checksum: 10c0/968bcf1c7c88c04df1ffb463c179558a2ec17aa49e49376120504958239d9e9dad5281aa05f2a78542b8557f2be0b0b4c325710262f3b838b40d703d5ed30c23 + languageName: node + linkType: hard + "validate-npm-package-license@npm:^3.0.1": version: 3.0.4 resolution: "validate-npm-package-license@npm:3.0.4" @@ -3623,16 +3941,16 @@ __metadata: linkType: hard "viem@npm:^2.21.25": - version: 2.21.32 - resolution: "viem@npm:2.21.32" + version: 2.21.51 + resolution: "viem@npm:2.21.51" dependencies: - "@adraffy/ens-normalize": "npm:1.11.0" "@noble/curves": "npm:1.6.0" "@noble/hashes": "npm:1.5.0" "@scure/bip32": "npm:1.5.0" "@scure/bip39": "npm:1.4.0" abitype: "npm:1.0.6" isows: "npm:1.0.6" + ox: "npm:0.1.2" webauthn-p256: "npm:0.0.10" ws: "npm:8.18.0" peerDependencies: @@ -3640,7 +3958,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/1a38f1fbb9e71eafd5d923d737f6869a700b32d12e5e735f9b7cea4692971ee869bb8f7e1a265ba80b81ba2e0e129137d868d33b2faba260d3a06915d75306a5 + checksum: 10c0/aca0e6ad3826ce16b7c8463f84063d382769b2fb21d5d1ef3794866e5939e7a73d0e108e0cc45e1d830449d18b935ae1f7d14821adf529586700e066a771cf65 languageName: node linkType: hard @@ -3667,16 +3985,49 @@ __metadata: languageName: node linkType: hard +"which-builtin-type@npm:^1.1.4": + version: 1.2.0 + resolution: "which-builtin-type@npm:1.2.0" + dependencies: + call-bind: "npm:^1.0.7" + function.prototype.name: "npm:^1.1.6" + has-tostringtag: "npm:^1.0.2" + is-async-function: "npm:^2.0.0" + is-date-object: "npm:^1.0.5" + is-finalizationregistry: "npm:^1.1.0" + is-generator-function: "npm:^1.0.10" + is-regex: "npm:^1.1.4" + is-weakref: "npm:^1.0.2" + isarray: "npm:^2.0.5" + which-boxed-primitive: "npm:^1.0.2" + which-collection: "npm:^1.0.2" + which-typed-array: "npm:^1.1.15" + checksum: 10c0/7cd4a8ccfa6a3cb7c2296c716e7266b9f31a66f3e131fe7b185232c16d3ad21442046ec1798c4ec1e19dce7eb99c7751377192e5e734dc07042d14ec0f09b332 + languageName: node + linkType: hard + +"which-collection@npm:^1.0.2": + version: 1.0.2 + resolution: "which-collection@npm:1.0.2" + dependencies: + is-map: "npm:^2.0.3" + is-set: "npm:^2.0.3" + is-weakmap: "npm:^2.0.2" + is-weakset: "npm:^2.0.3" + checksum: 10c0/3345fde20964525a04cdf7c4a96821f85f0cc198f1b2ecb4576e08096746d129eb133571998fe121c77782ac8f21cbd67745a3d35ce100d26d4e684c142ea1f2 + languageName: node + linkType: hard + "which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15": - version: 1.1.15 - resolution: "which-typed-array@npm:1.1.15" + version: 1.1.16 + resolution: "which-typed-array@npm:1.1.16" dependencies: available-typed-arrays: "npm:^1.0.7" call-bind: "npm:^1.0.7" for-each: "npm:^0.3.3" gopd: "npm:^1.0.1" has-tostringtag: "npm:^1.0.2" - checksum: 10c0/4465d5348c044032032251be54d8988270e69c6b7154f8fcb2a47ff706fe36f7624b3a24246b8d9089435a8f4ec48c1c1025c5d6b499456b9e5eff4f48212983 + checksum: 10c0/a9075293200db4fbce7c24d52731843542c5a19edfc66e31aa2cbefa788b5caa7ef05008f6e60d2c38d8198add6b92d0ddc2937918c5c308be398b1ebd8721af languageName: node linkType: hard @@ -3792,6 +4143,13 @@ __metadata: languageName: node linkType: hard +"yargs-parser@npm:^21.1.1": + version: 21.1.1 + resolution: "yargs-parser@npm:21.1.1" + checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2 + languageName: node + linkType: hard + "yargs-unparser@npm:^2.0.0": version: 2.0.0 resolution: "yargs-unparser@npm:2.0.0" @@ -3819,6 +4177,21 @@ __metadata: languageName: node linkType: hard +"yargs@npm:^17.7.2": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" + dependencies: + cliui: "npm:^8.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + require-directory: "npm:^2.1.1" + string-width: "npm:^4.2.3" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^21.1.1" + checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05 + languageName: node + linkType: hard + "yn@npm:3.1.1": version: 3.1.1 resolution: "yn@npm:3.1.1"