Skip to content

Commit

Permalink
feat: 馃幐 Add support for manual settlement
Browse files Browse the repository at this point in the history
  • Loading branch information
prashantasdeveloper committed Jan 30, 2023
1 parent 0ef1eee commit 075033d
Show file tree
Hide file tree
Showing 9 changed files with 293 additions and 90 deletions.
57 changes: 53 additions & 4 deletions src/api/entities/Instruction/__tests__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,10 +361,15 @@ describe('Instruction class', () => {
let bigNumberToU64Spy: jest.SpyInstance;
let queryMultiMock: jest.Mock;
let instructionMemoToStringSpy: jest.SpyInstance;
let meshSettlementTypeToEndConditionSpy: jest.SpyInstance;

beforeAll(() => {
bigNumberToU64Spy = jest.spyOn(utilsConversionModule, 'bigNumberToU64');
instructionMemoToStringSpy = jest.spyOn(utilsConversionModule, 'instructionMemoToString');
meshSettlementTypeToEndConditionSpy = jest.spyOn(
utilsConversionModule,
'meshSettlementTypeToEndCondition'
);
});

beforeEach(() => {
Expand All @@ -386,6 +391,7 @@ describe('Instruction class', () => {

entityMockUtils.configureMocks({ identityOptions: { did: owner } });

let rawSettlementType = dsMockUtils.createMockSettlementType(type);
const rawInstructionDetails = dsMockUtils.createMockInstruction({
instructionId: dsMockUtils.createMockU64(new BigNumber(1)),
status: dsMockUtils.createMockInstructionStatus(status),
Expand All @@ -399,8 +405,11 @@ describe('Instruction class', () => {
valueDate: dsMockUtils.createMockOption(
dsMockUtils.createMockMoment(new BigNumber(valueDate.getTime()))
),
settlementType: dsMockUtils.createMockSettlementType(type),
settlementType: rawSettlementType,
});
when(meshSettlementTypeToEndConditionSpy)
.calledWith(rawSettlementType)
.mockReturnValueOnce({ type });
const rawInstructionMemo = dsMockUtils.createMockInstructionMemo(memo);
const rawOptionalMemo = dsMockUtils.createMockOption(rawInstructionMemo);

Expand All @@ -423,14 +432,18 @@ describe('Instruction class', () => {
type = InstructionType.SettleOnBlock;
const endBlock = new BigNumber(100);

rawSettlementType = dsMockUtils.createMockSettlementType({
SettleOnBlock: dsMockUtils.createMockU32(endBlock),
});
when(meshSettlementTypeToEndConditionSpy)
.calledWith(rawSettlementType)
.mockReturnValueOnce({ type, endBlock });
queryMultiMock.mockResolvedValueOnce([
dsMockUtils.createMockInstruction({
...rawInstructionDetails,
tradeDate: dsMockUtils.createMockOption(),
valueDate: dsMockUtils.createMockOption(),
settlementType: dsMockUtils.createMockSettlementType({
SettleOnBlock: dsMockUtils.createMockU32(endBlock),
}),
settlementType: rawSettlementType,
}),
dsMockUtils.createMockOption(),
]);
Expand All @@ -448,9 +461,45 @@ describe('Instruction class', () => {
});
expect(result.venue.id).toEqual(venueId);

type = InstructionType.SettleManual;

rawSettlementType = dsMockUtils.createMockSettlementType({
SettleManual: dsMockUtils.createMockU32(endBlock),
});
when(meshSettlementTypeToEndConditionSpy)
.calledWith(rawSettlementType)
.mockReturnValueOnce({ type, endAfterBlock: endBlock });

queryMultiMock.mockResolvedValueOnce([
dsMockUtils.createMockInstruction({
...rawInstructionDetails,
tradeDate: dsMockUtils.createMockOption(),
valueDate: dsMockUtils.createMockOption(),
settlementType: rawSettlementType,
}),
dsMockUtils.createMockOption(),
]);

result = await instruction.details();

expect(result).toMatchObject({
status,
createdAt,
tradeDate: null,
valueDate: null,
type,
endAfterBlock: endBlock,
memo: null,
});
expect(result.venue.id).toEqual(venueId);

status = InstructionStatus.Failed;
type = InstructionType.SettleOnAffirmation;

when(meshSettlementTypeToEndConditionSpy)
.calledWith(rawSettlementType)
.mockReturnValueOnce({ type });

queryMultiMock.mockResolvedValueOnce([
dsMockUtils.createMockInstruction({
...rawInstructionDetails,
Expand Down
19 changes: 3 additions & 16 deletions src/api/entities/Instruction/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ import {
meshAffirmationStatusToAffirmationStatus,
meshInstructionStatusToInstructionStatus,
meshPortfolioIdToPortfolio,
meshSettlementTypeToEndCondition,
middlewareEventToEventIdentifier,
middlewareV2EventDetailsToEventIdentifier,
momentToDate,
tickerToString,
u32ToBigNumber,
u64ToBigNumber,
} from '~/utils/conversion';
import { createProcedureMethod, optionize, requestMulti, requestPaginated } from '~/utils/internal';
Expand All @@ -50,7 +50,6 @@ import {
InstructionDetails,
InstructionStatus,
InstructionStatusResult,
InstructionType,
Leg,
} from './types';

Expand Down Expand Up @@ -269,7 +268,7 @@ export class Instruction extends Entity<UniqueIdentifiers, string> {
});
}

const details = {
return {
status:
status === InternalInstructionStatus.Pending
? InstructionStatus.Pending
Expand All @@ -279,19 +278,7 @@ export class Instruction extends Entity<UniqueIdentifiers, string> {
valueDate: valueDate.isSome ? momentToDate(valueDate.unwrap()) : null,
venue: new Venue({ id: u64ToBigNumber(venueId) }, context),
memo: memo.isSome ? instructionMemoToString(memo.unwrap()) : null,
};

if (type.isSettleOnAffirmation) {
return {
...details,
type: InstructionType.SettleOnAffirmation,
};
}

return {
...details,
type: InstructionType.SettleOnBlock,
endBlock: u32ToBigNumber(type.asSettleOnBlock),
...meshSettlementTypeToEndCondition(type),
};
}

Expand Down
24 changes: 15 additions & 9 deletions src/api/entities/Instruction/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,22 @@ export enum InstructionStatus {
export enum InstructionType {
SettleOnAffirmation = 'SettleOnAffirmation',
SettleOnBlock = 'SettleOnBlock',
SettleManual = 'SettleManual',
}

export type InstructionEndCondition =
| {
type: InstructionType.SettleOnAffirmation;
}
| {
type: InstructionType.SettleOnBlock;
endBlock: BigNumber;
}
| {
type: InstructionType.SettleManual;
endAfterBlock: BigNumber;
};

export type InstructionDetails = {
status: InstructionStatus;
createdAt: Date;
Expand All @@ -27,15 +41,7 @@ export type InstructionDetails = {
valueDate: Date | null;
venue: Venue;
memo: string | null;
} & (
| {
type: InstructionType.SettleOnAffirmation;
}
| {
type: InstructionType.SettleOnBlock;
endBlock: BigNumber;
}
);
} & InstructionEndCondition;

export interface Leg {
from: DefaultPortfolio | NumberedPortfolio;
Expand Down
116 changes: 85 additions & 31 deletions src/api/procedures/__tests__/addInstruction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,18 @@ import {
prepareStorage,
Storage,
} from '~/api/procedures/addInstruction';
import { Context, DefaultPortfolio, Instruction, NumberedPortfolio } from '~/internal';
import {
Context,
DefaultPortfolio,
Instruction,
NumberedPortfolio,
PolymeshError,
} from '~/internal';
import { dsMockUtils, entityMockUtils, procedureMockUtils } from '~/testUtils/mocks';
import { Mocked } from '~/testUtils/types';
import {
ErrorCode,
InstructionEndCondition,
InstructionType,
PortfolioLike,
RoleType,
Expand Down Expand Up @@ -52,13 +59,7 @@ describe('addInstruction procedure', () => {
>;
let endConditionToSettlementTypeSpy: jest.SpyInstance<
PalletSettlementSettlementType,
[
(
| { type: InstructionType.SettleOnAffirmation }
| { type: InstructionType.SettleOnBlock; value: BigNumber }
),
Context
]
[InstructionEndCondition, Context]
>;
let dateToMomentSpy: jest.SpyInstance<Moment, [Date, Context]>;
let stringToInstructionMemoSpy: jest.SpyInstance;
Expand Down Expand Up @@ -88,6 +89,7 @@ describe('addInstruction procedure', () => {
let rawInstructionMemo: PalletSettlementInstructionMemo;
let rawAuthSettlementType: PalletSettlementSettlementType;
let rawBlockSettlementType: PalletSettlementSettlementType;
let rawManualSettlementType: PalletSettlementSettlementType;
let rawLeg: {
from: PolymeshPrimitivesIdentityIdPortfolioId;
to: PolymeshPrimitivesIdentityIdPortfolioId;
Expand Down Expand Up @@ -161,6 +163,7 @@ describe('addInstruction procedure', () => {
rawInstructionMemo = dsMockUtils.createMockInstructionMemo(memo);
rawAuthSettlementType = dsMockUtils.createMockSettlementType('SettleOnAffirmation');
rawBlockSettlementType = dsMockUtils.createMockSettlementType({ SettleOnBlock: rawEndBlock });
rawManualSettlementType = dsMockUtils.createMockSettlementType({ SettleManual: rawEndBlock });
rawLeg = {
from: rawFrom,
to: rawTo,
Expand Down Expand Up @@ -240,8 +243,11 @@ describe('addInstruction procedure', () => {
when(bigNumberToU64Spy).calledWith(venueId, mockContext).mockReturnValue(rawVenueId);
when(bigNumberToBalanceSpy).calledWith(amount, mockContext).mockReturnValue(rawAmount);
when(endConditionToSettlementTypeSpy)
.calledWith({ type: InstructionType.SettleOnBlock, value: endBlock }, mockContext)
.calledWith({ type: InstructionType.SettleOnBlock, endBlock }, mockContext)
.mockReturnValue(rawBlockSettlementType);
when(endConditionToSettlementTypeSpy)
.calledWith({ type: InstructionType.SettleManual, endAfterBlock: endBlock }, mockContext)
.mockReturnValue(rawManualSettlementType);
when(endConditionToSettlementTypeSpy)
.calledWith({ type: InstructionType.SettleOnAffirmation }, mockContext)
.mockReturnValue(rawAuthSettlementType);
Expand Down Expand Up @@ -437,10 +443,14 @@ describe('addInstruction procedure', () => {
portfoliosToAffirm: [],
});

let error;
const expectedError = new PolymeshError({
code: ErrorCode.ValidationError,
message: 'End block must be a future block',
data: { failedInstructionIndexes: [0] },
});

try {
await prepareAddInstruction.call(proc, {
await expect(
prepareAddInstruction.call(proc, {
venueId,
instructions: [
{
Expand All @@ -455,14 +465,27 @@ describe('addInstruction procedure', () => {
endBlock: new BigNumber(100),
},
],
});
} catch (err) {
error = err;
}
})
).rejects.toThrowError(expectedError);

expect(error.message).toBe('End block must be a future block');
expect(error.code).toBe(ErrorCode.ValidationError);
expect(error.data.failedInstructionIndexes[0]).toBe(0);
await expect(
prepareAddInstruction.call(proc, {
venueId,
instructions: [
{
legs: [
{
from,
to,
amount,
asset: entityMockUtils.getAssetInstance({ ticker: asset }),
},
],
endAfterBlock: new BigNumber(100),
},
],
})
).rejects.toThrowError(expectedError);
});

it('should throw an error if the value date is before the trade date', async () => {
Expand Down Expand Up @@ -542,22 +565,26 @@ describe('addInstruction procedure', () => {
portfoliosToAffirm: [[]],
});

const result = await prepareAddInstruction.call(proc, {
const instructionDetails = {
legs: [
{
from,
to,
amount,
asset: entityMockUtils.getAssetInstance({ ticker: asset }),
},
],
tradeDate,
valueDate,
memo,
};

let result = await prepareAddInstruction.call(proc, {
venueId,
instructions: [
{
legs: [
{
from,
to,
amount,
asset: entityMockUtils.getAssetInstance({ ticker: asset }),
},
],
tradeDate,
valueDate,
...instructionDetails,
endBlock,
memo,
},
],
});
Expand All @@ -578,6 +605,33 @@ describe('addInstruction procedure', () => {
],
resolver: expect.any(Function),
});

result = await prepareAddInstruction.call(proc, {
venueId,
instructions: [
{
...instructionDetails,
endAfterBlock: endBlock,
},
],
});

expect(result).toEqual({
transactions: [
{
transaction: addInstructionTransaction,
args: [
rawVenueId,
rawManualSettlementType,
rawTradeDate,
rawValueDate,
[rawLeg],
rawInstructionMemo,
],
},
],
resolver: expect.any(Function),
});
});

describe('getAuthorization', () => {
Expand Down

0 comments on commit 075033d

Please sign in to comment.