diff --git a/src/api/entities/Portfolio/__tests__/index.ts b/src/api/entities/Portfolio/__tests__/index.ts index 9fe976a87e..7f7c20cd0a 100644 --- a/src/api/entities/Portfolio/__tests__/index.ts +++ b/src/api/entities/Portfolio/__tests__/index.ts @@ -7,7 +7,7 @@ import { Entity, Identity, Portfolio, SecurityToken } from '~/api/entities'; import { NumberedPortfolio } from '~/api/entities/NumberedPortfolio'; import { moveFunds } from '~/api/procedures'; import { Context, TransactionQueue } from '~/base'; -import { dsMockUtils } from '~/testUtils/mocks'; +import { dsMockUtils, entityMockUtils } from '~/testUtils/mocks'; import { tuple } from '~/types/utils'; import * as utilsModule from '~/utils'; @@ -77,6 +77,53 @@ describe('Portfolio class', () => { }); }); + describe('method: isCustodiedBy', () => { + let did: string; + let id: BigNumber; + + beforeAll(() => { + did = 'someDid'; + id = new BigNumber(1); + sinon.stub(utilsModule, 'portfolioIdToMeshPortfolioId'); + }); + + afterAll(() => { + sinon.restore(); + }); + + test('should return the custodian of the portfolio', async () => { + const portfolio = new Portfolio({ did, id }, context); + const custodianDid = 'custodianDid'; + const currentIdentityDid = 'currentIdentity'; + const identityIdToStringStub = sinon.stub(utilsModule, 'identityIdToString'); + const portfolioCustodianStub = dsMockUtils.createQueryStub('portfolio', 'portfolioCustodian'); + + portfolioCustodianStub.returns( + dsMockUtils.createMockOption(dsMockUtils.createMockIdentityId(custodianDid)) + ); + identityIdToStringStub.returns(custodianDid); + + let result = await portfolio.isCustodiedBy(); + expect(result).toEqual(false); + + portfolioCustodianStub.returns( + dsMockUtils.createMockOption(dsMockUtils.createMockIdentityId(currentIdentityDid)) + ); + identityIdToStringStub.returns(currentIdentityDid); + + result = await portfolio.isCustodiedBy({ identity: currentIdentityDid }); + expect(result).toEqual(true); + + dsMockUtils.createQueryStub('portfolio', 'portfolioCustodian').returns({}); + + result = await portfolio.isCustodiedBy({ identity: 'otherDid' }); + expect(result).toEqual(false); + + result = await portfolio.isCustodiedBy({ identity: 'someDid' }); + expect(result).toEqual(true); + }); + }); + describe('method: getTokenBalances', () => { let did: string; let id: BigNumber; @@ -187,43 +234,43 @@ describe('Portfolio class', () => { expect(queue).toBe(expectedQueue); }); + }); - describe('method: getCustodian', () => { - let did: string; - let id: BigNumber; + describe('method: getCustodian', () => { + let did: string; + let id: BigNumber; - beforeAll(() => { - did = 'someDid'; - id = new BigNumber(1); - sinon.stub(utilsModule, 'portfolioIdToMeshPortfolioId'); - }); + beforeAll(() => { + did = 'someDid'; + id = new BigNumber(1); + sinon.stub(utilsModule, 'portfolioIdToMeshPortfolioId'); + }); - afterAll(() => { - sinon.restore(); - }); + afterAll(() => { + sinon.restore(); + }); - test('should return the custodian of the portfolio', async () => { - const custodianDid = 'custodianDid'; - const identityIdToStringStub = sinon.stub(utilsModule, 'identityIdToString'); + test('should return the custodian of the portfolio', async () => { + const custodianDid = 'custodianDid'; + const identityIdToStringStub = sinon.stub(utilsModule, 'identityIdToString'); - dsMockUtils - .createQueryStub('portfolio', 'portfolioCustodian') - .returns(dsMockUtils.createMockOption(dsMockUtils.createMockIdentityId(custodianDid))); + dsMockUtils + .createQueryStub('portfolio', 'portfolioCustodian') + .returns(dsMockUtils.createMockOption(dsMockUtils.createMockIdentityId(custodianDid))); - identityIdToStringStub.returns(custodianDid); + identityIdToStringStub.returns(custodianDid); - const portfolio = new Portfolio({ did, id }, context); + const portfolio = new Portfolio({ did, id }, context); - let result = await portfolio.getCustodian(); - expect(result.did).toEqual(custodianDid); + let result = await portfolio.getCustodian(); + expect(result.did).toEqual(custodianDid); - dsMockUtils.createQueryStub('portfolio', 'portfolioCustodian').returns({}); + dsMockUtils.createQueryStub('portfolio', 'portfolioCustodian').returns({}); - identityIdToStringStub.returns(did); + identityIdToStringStub.returns(did); - result = await portfolio.getCustodian(); - expect(result.did).toEqual(did); - }); + result = await portfolio.getCustodian(); + expect(result.did).toEqual(did); }); }); }); diff --git a/src/api/entities/Portfolio/index.ts b/src/api/entities/Portfolio/index.ts index 1a221402d0..199bf241ee 100644 --- a/src/api/entities/Portfolio/index.ts +++ b/src/api/entities/Portfolio/index.ts @@ -72,6 +72,38 @@ export class Portfolio extends Entity { return ownerDid === did; } + /** + * Return whether an Identity is the Portfolio custodian + * + * @param args.identity - defaults to the current Identity + */ + public async isCustodiedBy(args?: { identity: string | Identity }): Promise { + const { + _id, + owner: { did }, + context, + context: { + polymeshApi: { + query: { portfolio }, + }, + }, + } = this; + + const rawPortfolioId = portfolioIdToMeshPortfolioId({ did, number: _id }, context); + + const [portfolioCustodian, targetDid] = await Promise.all([ + portfolio.portfolioCustodian(rawPortfolioId), + getDid(args?.identity, context), + ]); + + try { + const rawIdentityId = portfolioCustodian.unwrap(); + return targetDid === identityIdToString(rawIdentityId); + } catch (_) { + return targetDid === did; + } + } + /** * Retrieve the balances of all assets in this Portfolio *