From 2dc9486bf49404652976eddf84721d6d7279724c Mon Sep 17 00:00:00 2001 From: Prashant Bajpai <34747455+prashantasdeveloper@users.noreply.github.com> Date: Thu, 28 Nov 2024 13:23:56 +0530 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9B=20Correctly=20handle=20curr?= =?UTF-8?q?ency=20as=20ticker=20when=20configuring=20DD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When currency was passed as a ticker value, asset balances were not correctly fetched, causing the procedure to fail erroneously. This fixes the logic for getting asset balances as well as handles currency being passed as a ticker value as well. --- src/api/entities/Portfolio/__tests__/index.ts | 13 +++++++ src/api/entities/Portfolio/index.ts | 30 ++++++++-------- .../configureDividendDistribution.ts | 5 +++ .../configureDividendDistribution.ts | 14 +++++--- src/utils/__tests__/internal.ts | 35 +++++++++++++++---- src/utils/internal.ts | 9 +++-- 6 files changed, 77 insertions(+), 29 deletions(-) diff --git a/src/api/entities/Portfolio/__tests__/index.ts b/src/api/entities/Portfolio/__tests__/index.ts index 801da0a006..0744e67e2d 100644 --- a/src/api/entities/Portfolio/__tests__/index.ts +++ b/src/api/entities/Portfolio/__tests__/index.ts @@ -200,8 +200,10 @@ describe('Portfolio class', () => { let rawLocked1: Balance; let rawLocked2: Balance; let rawPortfolioId: PolymeshPrimitivesIdentityIdPortfolioId; + let asFungibleAssetSpy: jest.SpyInstance; beforeAll(() => { + asFungibleAssetSpy = jest.spyOn(utilsInternalModule, 'asFungibleAsset'); did = 'someDid'; id = new BigNumber(1); assetId0 = '0x11111111111181111111111111111111'; @@ -269,6 +271,17 @@ describe('Portfolio class', () => { const portfolio = new NonAbstract({ did, id }, context); const otherAssetId = '0x99999999999999999999999999999999'; + + when(asFungibleAssetSpy) + .calledWith(hexToUuid(assetId0), context) + .mockResolvedValue( + entityMockUtils.getFungibleAssetInstance({ assetId: hexToUuid(assetId0) }) + ); + + when(asFungibleAssetSpy) + .calledWith(otherAssetId, context) + .mockResolvedValue(entityMockUtils.getFungibleAssetInstance({ assetId: otherAssetId })); + const result = await portfolio.getAssetBalances({ assets: [hexToUuid(assetId0), new FungibleAsset({ assetId: otherAssetId }, context)], }); diff --git a/src/api/entities/Portfolio/index.ts b/src/api/entities/Portfolio/index.ts index 70a27d8e9b..3a3b983e1a 100644 --- a/src/api/entities/Portfolio/index.ts +++ b/src/api/entities/Portfolio/index.ts @@ -193,21 +193,21 @@ export abstract class Portfolio extends Entity } }); - const mask: PortfolioBalance[] | undefined = args?.assets.map(asset => ({ - total: new BigNumber(0), - locked: new BigNumber(0), - free: new BigNumber(0), - asset: asFungibleAsset(asset, context), - })); - - if (mask) { - return mask.map(portfolioBalance => { - const { - asset: { id: assetId }, - } = portfolioBalance; - - return assetBalances[assetId] ?? portfolioBalance; - }); + if (args?.assets.length) { + const filteredBalances: PortfolioBalance[] = []; + for (const asset of args.assets) { + const argAsset = await asFungibleAsset(asset, context); + const portfolioBalance = { + total: new BigNumber(0), + locked: new BigNumber(0), + free: new BigNumber(0), + asset: argAsset, + }; + + filteredBalances.push(assetBalances[argAsset.id] ?? portfolioBalance); + } + + return filteredBalances; } return values(assetBalances); diff --git a/src/api/procedures/__tests__/configureDividendDistribution.ts b/src/api/procedures/__tests__/configureDividendDistribution.ts index 3b0cfa63c5..b7d9638ed6 100644 --- a/src/api/procedures/__tests__/configureDividendDistribution.ts +++ b/src/api/procedures/__tests__/configureDividendDistribution.ts @@ -94,6 +94,7 @@ describe('configureDividendDistribution procedure', () => { let dateToMomentSpy: jest.SpyInstance; let bigNumberToBalanceSpy: jest.SpyInstance; let corporateActionParamsToMeshCorporateActionArgsSpy: jest.SpyInstance; + let asFungibleAssetSpy: jest.SpyInstance; beforeAll(() => { entityMockUtils.initMocks(); @@ -159,6 +160,7 @@ describe('configureDividendDistribution procedure', () => { utilsConversionModule, 'corporateActionParamsToMeshCorporateActionArgs' ); + asFungibleAssetSpy = jest.spyOn(utilsInternalModule, 'asFungibleAsset'); }); beforeEach(() => { @@ -169,6 +171,9 @@ describe('configureDividendDistribution procedure', () => { mockContext = dsMockUtils.getContextInstance(); + when(asFungibleAssetSpy) + .calledWith(currency, mockContext) + .mockResolvedValue(entityMockUtils.getFungibleAssetInstance({ assetId: currency })); when(assetToMeshAssetIdSpy) .calledWith(expect.objectContaining({ id: currency }), mockContext) .mockReturnValue(rawCurrency); diff --git a/src/api/procedures/configureDividendDistribution.ts b/src/api/procedures/configureDividendDistribution.ts index a4e4d075ca..c9d81c9468 100644 --- a/src/api/procedures/configureDividendDistribution.ts +++ b/src/api/procedures/configureDividendDistribution.ts @@ -3,7 +3,6 @@ import BigNumber from 'bignumber.js'; import { assertDistributionDatesValid } from '~/api/procedures/utils'; import { - BaseAsset, Checkpoint, Context, DefaultPortfolio, @@ -33,7 +32,12 @@ import { portfolioToPortfolioId, u32ToBigNumber, } from '~/utils/conversion'; -import { filterEventRecords, getCheckpointValue, optionize } from '~/utils/internal'; +import { + asFungibleAsset, + filterEventRecords, + getCheckpointValue, + optionize, +} from '~/utils/internal'; /** * @hidden @@ -174,7 +178,9 @@ export async function prepareConfigureDividendDistribution( } } - const [{ free }] = await portfolio.getAssetBalances({ assets: [currency] }); + const currencyAsset = await asFungibleAsset(currency, context); + + const [{ free }] = await portfolio.getAssetBalances({ assets: [currencyAsset] }); if (free.lt(maxAmount)) { throw new PolymeshError({ @@ -192,7 +198,7 @@ export async function prepareConfigureDividendDistribution( originPortfolio instanceof BigNumber ? originPortfolio : originPortfolio.id, context ); - const rawCurrency = assetToMeshAssetId(new BaseAsset({ assetId: currency }, context), context); + const rawCurrency = assetToMeshAssetId(currencyAsset, context); const rawPerShare = bigNumberToBalance(perShare, context); const rawAmount = bigNumberToBalance(maxAmount, context); diff --git a/src/utils/__tests__/internal.ts b/src/utils/__tests__/internal.ts index 2afc605503..7790af1f4a 100644 --- a/src/utils/__tests__/internal.ts +++ b/src/utils/__tests__/internal.ts @@ -121,6 +121,11 @@ jest.mock( '~/api/entities/Asset/Fungible', require('~/testUtils/mocks/entities').mockFungibleAssetModule('~/api/entities/Asset/Fungible') ); + +jest.mock( + '~/api/entities/Asset/Base/BaseAsset', + require('~/testUtils/mocks/entities').mockBaseAssetModule('~/api/entities/Asset/Base/BaseAsset') +); jest.mock( '~/api/entities/Asset/NonFungible', require('~/testUtils/mocks/entities').mockNftCollectionModule('~/api/entities/Asset/NonFungible') @@ -2314,30 +2319,46 @@ describe('asChildIdentity', () => { }); describe('asFungibleAsset', () => { - it('should return a given FungibleAsset', () => { + beforeAll(() => { + dsMockUtils.initMocks(); + }); + + afterEach(() => { + dsMockUtils.reset(); + }); + + afterAll(() => { + dsMockUtils.cleanup(); + }); + + it('should return a given FungibleAsset', async () => { const mockContext = dsMockUtils.getContextInstance(); const input = entityMockUtils.getFungibleAssetInstance(); - const result = asFungibleAsset(input, mockContext); + const result = await asFungibleAsset(input, mockContext); expect(result).toEqual(input); }); - it('should create a new FungibleAsset given an asset ID', () => { + it('should create a new FungibleAsset given an asset ID', async () => { const mockContext = dsMockUtils.getContextInstance(); const assetId = '0x12341234123412341234123412341234'; - const result = asFungibleAsset(assetId, mockContext); + dsMockUtils + .createQueryMock('asset', 'assetIdTicker') + .mockResolvedValue(dsMockUtils.createMockTicker('TICKER')); - expect(result).toEqual(expect.objectContaining({ id: assetId })); + const result = await asFungibleAsset(assetId, mockContext); + + expect(result).toEqual(expect.objectContaining({ id: hexToUuid(assetId) })); }); - it('should create a new FungibleAsset given a BaseAsset', () => { + it('should create a new FungibleAsset given a BaseAsset', async () => { const mockContext = dsMockUtils.getContextInstance(); const assetId = '0x12341234123412341234123412341234'; const baseAsset = entityMockUtils.getBaseAssetInstance({ assetId }); - const result = asFungibleAsset(baseAsset, mockContext); + const result = await asFungibleAsset(baseAsset, mockContext); expect(result).toEqual(expect.objectContaining({ id: assetId })); }); diff --git a/src/utils/internal.ts b/src/utils/internal.ts index 332df72e34..8bcf04057e 100644 --- a/src/utils/internal.ts +++ b/src/utils/internal.ts @@ -1154,12 +1154,15 @@ export async function asAsset(asset: string | Asset, context: Context): Promise< * @hidden * Transforms asset or ticker into a `FungibleAsset` entity */ -export function asFungibleAsset(asset: string | BaseAsset, context: Context): FungibleAsset { +export async function asFungibleAsset( + asset: string | BaseAsset, + context: Context +): Promise { if (asset instanceof FungibleAsset) { return asset; } - const assetId = typeof asset === 'string' ? asset : asset.id; + const assetId = await asAssetId(asset, context); return new FungibleAsset({ assetId }, context); } @@ -1311,7 +1314,7 @@ export async function getCheckpointValue( ) { return checkpoint; } - const assetEntity = asFungibleAsset(asset, context); + const assetEntity = await asFungibleAsset(asset, context); const { type, id } = checkpoint; if (type === CaCheckpointType.Existing) { return assetEntity.checkpoints.getOne({ id });