Skip to content

Commit 090e435

Browse files
committed
test: e2e REST manual settlements
1 parent 7b8c0d2 commit 090e435

File tree

4 files changed

+329
-3
lines changed

4 files changed

+329
-3
lines changed
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
import { BigNumber } from '@polymeshassociation/polymesh-sdk';
2+
import { expectBasicTxInfo } from '~/__tests__/rest/utils';
3+
import { TestFactory } from '~/helpers';
4+
import { RestClient } from '~/rest';
5+
import { createAssetParams } from '~/rest/assets/params';
6+
import { ProcessMode } from '~/rest/common';
7+
import { Identity } from '~/rest/identities/interfaces';
8+
import { RestSuccessResult } from '~/rest/interfaces';
9+
import { fungibleInstructionParams, venueParams } from '~/rest/settlements';
10+
import { awaitMiddlewareSyncedForRestApi } from '~/util';
11+
12+
const handles = ['issuer', 'investor'];
13+
let factory: TestFactory;
14+
15+
describe('Settlements - REST API (Manual Settlement Flow)', () => {
16+
let restClient: RestClient;
17+
let issuer: Identity;
18+
let investor: Identity;
19+
let signer: string;
20+
let assetParams: ReturnType<typeof createAssetParams>;
21+
let assetId: string;
22+
let venueId: string;
23+
let instructionId: string;
24+
let createInstructionParams: ReturnType<typeof fungibleInstructionParams>;
25+
let endAfterBlock: string;
26+
27+
beforeAll(async () => {
28+
factory = await TestFactory.create({ handles });
29+
({ restClient } = factory);
30+
issuer = factory.getSignerIdentity(handles[0]);
31+
investor = factory.getSignerIdentity(handles[1]);
32+
signer = issuer.signer;
33+
34+
assetParams = createAssetParams({
35+
options: { processMode: ProcessMode.Submit, signer },
36+
});
37+
});
38+
39+
afterAll(async () => {
40+
await factory.close();
41+
});
42+
43+
it('should create a fungible asset with initial supply', async () => {
44+
assetId = await restClient.assets.createAndGetAssetId(assetParams);
45+
46+
createInstructionParams = fungibleInstructionParams(assetId, issuer.did, investor.did, {
47+
options: { processMode: ProcessMode.Submit, signer },
48+
});
49+
50+
expect(assetId).toBeTruthy();
51+
});
52+
53+
it('should create a venue, fetch details and update venue', async () => {
54+
const venueTx = await restClient.settlements.createVenue(
55+
venueParams({
56+
options: { signer, processMode: ProcessMode.Submit },
57+
})
58+
);
59+
60+
expect(venueTx).toMatchObject({
61+
transactions: expect.arrayContaining([
62+
{
63+
transactionTag: 'settlement.createVenue',
64+
type: 'single',
65+
...expectBasicTxInfo,
66+
},
67+
]),
68+
});
69+
70+
venueId = (venueTx as RestSuccessResult).venue as string;
71+
expect(venueId).toBeTruthy();
72+
73+
let venueDetails = await restClient.settlements.getVenue(venueId);
74+
expect(venueDetails).toMatchObject({
75+
description: expect.any(String),
76+
type: 'Exchange',
77+
owner: issuer.did,
78+
});
79+
80+
const updatedVenueTx = await restClient.settlements.updateVenue(
81+
venueId,
82+
{
83+
description: 'Updated Venue Description',
84+
type: 'Other',
85+
},
86+
{
87+
options: { signer, processMode: ProcessMode.Submit },
88+
}
89+
);
90+
91+
expect(updatedVenueTx).toMatchObject({
92+
transactions: expect.arrayContaining([
93+
{
94+
transactionTags: ['settlement.updateVenueDetails', 'settlement.updateVenueType'],
95+
type: 'batch',
96+
...expectBasicTxInfo,
97+
},
98+
]),
99+
});
100+
101+
venueDetails = await restClient.settlements.getVenue(venueId);
102+
expect(venueDetails).toMatchObject({
103+
description: 'Updated Venue Description',
104+
type: 'Other',
105+
});
106+
});
107+
108+
// TODO: dryRun needs to be checked - it doesn't seralize the return value correctly -> results in 500 error
109+
it.skip('should check if the instruction will run using dry run', async () => {
110+
const dryRunInstruction = await restClient.settlements.createInstruction(venueId, {
111+
...createInstructionParams,
112+
options: { processMode: ProcessMode.DryRun, signer },
113+
});
114+
115+
expect(dryRunInstruction).toMatchObject({
116+
transactions: expect.arrayContaining([
117+
{
118+
transactionTag: 'settlement.createInstruction',
119+
type: 'single',
120+
...expectBasicTxInfo,
121+
},
122+
]),
123+
});
124+
});
125+
126+
it('should create a settlement instruction', async () => {
127+
const createInstructionTx = await restClient.settlements.createInstruction(venueId, {
128+
...createInstructionParams,
129+
options: { processMode: ProcessMode.Submit, signer },
130+
});
131+
132+
expect(createInstructionTx).toMatchObject({
133+
transactions: expect.arrayContaining([
134+
{
135+
transactionTag: 'settlement.addAndAffirmWithMediators',
136+
type: 'single',
137+
...expectBasicTxInfo,
138+
},
139+
]),
140+
});
141+
142+
instructionId = (createInstructionTx as RestSuccessResult).instruction as string;
143+
expect(instructionId).toBeTruthy();
144+
});
145+
146+
it('should reject the instruction via receiver', async () => {
147+
const rejectInstructionTx = await restClient.settlements.rejectInstruction(instructionId, {
148+
options: { processMode: ProcessMode.Submit, signer: investor.signer },
149+
});
150+
151+
expect(rejectInstructionTx).toMatchObject({
152+
transactions: expect.arrayContaining([
153+
{
154+
transactionTag: 'settlement.rejectInstructionWithCount',
155+
type: 'single',
156+
...expectBasicTxInfo,
157+
},
158+
]),
159+
});
160+
});
161+
162+
it('should create a instruction to be settled manually', async () => {
163+
const latestBlock = await restClient.network.getLatestBlock();
164+
endAfterBlock = (Number(latestBlock.id) + 5).toString();
165+
166+
const createInstructionResult = await restClient.settlements.createInstruction(venueId, {
167+
...createInstructionParams,
168+
endAfterBlock: endAfterBlock.toString(),
169+
options: { processMode: ProcessMode.Submit, signer },
170+
});
171+
172+
expect(createInstructionResult).toMatchObject({
173+
transactions: expect.arrayContaining([
174+
{
175+
transactionTag: 'settlement.addAndAffirmWithMediators',
176+
type: 'single',
177+
...expectBasicTxInfo,
178+
},
179+
]),
180+
});
181+
182+
instructionId = (createInstructionResult as RestSuccessResult).instruction as string;
183+
expect(parseInt(instructionId)).toEqual(expect.any(Number));
184+
185+
await awaitMiddlewareSyncedForRestApi(createInstructionResult, restClient, new BigNumber(1));
186+
});
187+
188+
it('should get the instruction details, legs and status', async () => {
189+
const instructionDetails = await restClient.settlements.getInstruction(instructionId);
190+
191+
expect(instructionDetails).toMatchObject({
192+
venue: venueId,
193+
// TODO: This is not correct, REST API maps endAfterBlock to endBlock, it should be endAfterBlock
194+
endBlock: endAfterBlock,
195+
status: 'Pending',
196+
type: 'SettleManual',
197+
legs: expect.arrayContaining([
198+
{
199+
asset: assetId,
200+
amount: '10',
201+
from: {
202+
did: issuer.did,
203+
},
204+
to: {
205+
did: investor.did,
206+
},
207+
type: 'onChain',
208+
},
209+
]),
210+
});
211+
});
212+
213+
it('should approve the instruction via receiver', async () => {
214+
const approveInstructionTx = await restClient.settlements.affirmInstruction(instructionId, {
215+
options: { processMode: ProcessMode.Submit, signer: investor.signer },
216+
});
217+
218+
expect(approveInstructionTx).toMatchObject({
219+
transactions: expect.arrayContaining([
220+
{
221+
transactionTag: 'settlement.affirmInstructionWithCount',
222+
type: 'single',
223+
...expectBasicTxInfo,
224+
},
225+
]),
226+
});
227+
228+
await awaitMiddlewareSyncedForRestApi(approveInstructionTx, restClient, new BigNumber(1));
229+
230+
const results = await restClient.settlements.getAffirmations(instructionId);
231+
232+
expect(results).toMatchObject({
233+
results: expect.arrayContaining([
234+
{
235+
identity: issuer.did,
236+
status: 'Affirmed',
237+
},
238+
{
239+
identity: investor.did,
240+
status: 'Affirmed',
241+
},
242+
]),
243+
total: '2',
244+
});
245+
});
246+
247+
it('should withdraw affirmation via receiver', async () => {
248+
const withdrawAffirmationTx = await restClient.settlements.withdrawAffirmation(instructionId, {
249+
options: { processMode: ProcessMode.Submit, signer: investor.signer },
250+
});
251+
252+
await awaitMiddlewareSyncedForRestApi(withdrawAffirmationTx, restClient, new BigNumber(1));
253+
254+
const result = await restClient.settlements.getAffirmations(instructionId);
255+
expect(result).toMatchObject({
256+
results: expect.arrayContaining([
257+
{
258+
identity: issuer.did,
259+
status: 'Affirmed',
260+
},
261+
]),
262+
total: '1',
263+
});
264+
});
265+
266+
it('should execute the instruction manually', async () => {
267+
const affirmResult = await restClient.settlements.affirmInstruction(instructionId, {
268+
options: { processMode: ProcessMode.Submit, signer: investor.signer },
269+
});
270+
271+
await awaitMiddlewareSyncedForRestApi(affirmResult, restClient, new BigNumber(1));
272+
273+
const { results } = await restClient.settlements.getPendingInstructions(investor.did);
274+
expect(results).toHaveLength(0);
275+
276+
const executeInstructionTx = await restClient.settlements.executeInstructionManually(
277+
instructionId,
278+
{
279+
options: { processMode: ProcessMode.Submit, signer: investor.signer },
280+
}
281+
);
282+
283+
expect(executeInstructionTx).toMatchObject({
284+
transactions: expect.arrayContaining([
285+
{
286+
transactionTag: 'settlement.executeManualInstruction',
287+
type: 'single',
288+
...expectBasicTxInfo,
289+
},
290+
]),
291+
});
292+
});
293+
});

tests/src/rest/interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface PolymeshLocalSettings {
2020

2121
export interface ResultSet<T> {
2222
results: T[];
23+
total: string;
2324
}
2425
interface SingleResult {
2526
type: 'single';

tests/src/rest/settlements/client.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { RestClient } from '~/rest/client';
22
import { TxBase } from '~/rest/common';
3-
import { PostResult } from '~/rest/interfaces';
3+
import { PostResult, ResultSet } from '~/rest/interfaces';
44
import {
55
fungibleInstructionParams,
66
nftInstructionParams,
@@ -72,10 +72,36 @@ export class Settlements {
7272
});
7373
}
7474

75-
public async getAffirmations(instructionId: string): Promise<unknown> {
75+
public async getAffirmations(
76+
instructionId: string
77+
): Promise<ResultSet<{ identity: string; status: string }>> {
7678
return this.client.get(`/instructions/${instructionId}/affirmations`);
7779
}
7880

81+
public async getVenue(venueId: string): Promise<unknown> {
82+
return this.client.get(`/venues/${venueId}`);
83+
}
84+
85+
public async updateVenue(
86+
venueId: string,
87+
params: { description?: string; type?: string },
88+
txBase: TxBase
89+
): Promise<PostResult> {
90+
return this.client.post(`/venues/${venueId}/modify`, {
91+
...txBase,
92+
...params,
93+
});
94+
}
95+
96+
public async executeInstructionManually(
97+
instructionId: string,
98+
txBase: TxBase
99+
): Promise<PostResult> {
100+
return this.client.post(`/instructions/${instructionId}/execute-manually`, {
101+
...txBase,
102+
});
103+
}
104+
79105
public async validateLeg({
80106
asset,
81107
toPortfolio,
@@ -95,4 +121,8 @@ export class Settlements {
95121
`/leg-validations?asset=${asset}&toPortfolio=${toPortfolio}&toDid=${toDid}&fromPortfolio=${fromPortfolio}&fromDid=${fromDid}&amount=${amount}`
96122
);
97123
}
124+
125+
public async getPendingInstructions(did: string): Promise<ResultSet<{ id: string }>> {
126+
return this.client.get(`/identities/${did}/pending-instructions`);
127+
}
98128
}

tests/src/rest/settlements/params.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export const fungibleInstructionParams = (
1313
from: string,
1414
to: string,
1515
base: TxBase,
16-
extras: TxExtras = {}
16+
extras: TxExtras = {},
17+
endAfterBlock?: string
1718
) =>
1819
({
1920
memo: 'Testing settlements',
@@ -32,6 +33,7 @@ export const fungibleInstructionParams = (
3233
asset: assetId,
3334
},
3435
],
36+
endAfterBlock,
3537
...extras,
3638
...base,
3739
} as const);

0 commit comments

Comments
 (0)