Skip to content

Commit

Permalink
fix: 🐛 Correctly handle currency as ticker when configuring DD
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
prashantasdeveloper committed Nov 28, 2024
1 parent c40df72 commit 2dc9486
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 29 deletions.
13 changes: 13 additions & 0 deletions src/api/entities/Portfolio/__tests__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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)],
});
Expand Down
30 changes: 15 additions & 15 deletions src/api/entities/Portfolio/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,21 +193,21 @@ export abstract class Portfolio extends Entity<UniqueIdentifiers, HumanReadable>
}
});

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);
Expand Down
5 changes: 5 additions & 0 deletions src/api/procedures/__tests__/configureDividendDistribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -159,6 +160,7 @@ describe('configureDividendDistribution procedure', () => {
utilsConversionModule,
'corporateActionParamsToMeshCorporateActionArgs'
);
asFungibleAssetSpy = jest.spyOn(utilsInternalModule, 'asFungibleAsset');
});

beforeEach(() => {
Expand All @@ -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);
Expand Down
14 changes: 10 additions & 4 deletions src/api/procedures/configureDividendDistribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import BigNumber from 'bignumber.js';

import { assertDistributionDatesValid } from '~/api/procedures/utils';
import {
BaseAsset,
Checkpoint,
Context,
DefaultPortfolio,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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({
Expand All @@ -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);
Expand Down
35 changes: 28 additions & 7 deletions src/utils/__tests__/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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 }));
});
Expand Down
9 changes: 6 additions & 3 deletions src/utils/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<FungibleAsset> {
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);
}
Expand Down Expand Up @@ -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 });
Expand Down

0 comments on commit 2dc9486

Please sign in to comment.