Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
},
"dependencies": {
"@polymeshassociation/local-signing-manager": "^3.5.2",
"@polymeshassociation/polymesh-sdk": "^27.5.1",
"@polymeshassociation/polymesh-sdk": "^28.0.0",
"@polymeshassociation/polymesh-types": "^6.2.0",
"cross-fetch": "^4.1.0",
"dotenv": "^16.5.0"
},
Expand Down
195 changes: 102 additions & 93 deletions tests/src/__tests__/rest/assets/controllerTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,112 +5,121 @@ import { ProcessMode } from '~/rest/common';
import { Identity } from '~/rest/identities/interfaces';
import { RestSuccessResult } from '~/rest/interfaces';
import { fungibleInstructionParams } from '~/rest/settlements/params';

import { expectBasicTxInfo } from '../utils';

const handles = ['issuer', 'holder'];
let factory: TestFactory;

describe('Fungible AssetController transfer', () => {
let restClient: RestClient;
let signer: string;
let issuer: Identity;
let holder: Identity;
let assetParams: ReturnType<typeof createAssetParams>;
let assetId: string;

beforeAll(async () => {
factory = await TestFactory.create({ handles });
({ restClient } = factory);
issuer = factory.getSignerIdentity(handles[0]);
holder = factory.getSignerIdentity(handles[1]);

signer = issuer.signer;

assetParams = createAssetParams({
options: { processMode: ProcessMode.Submit, signer },
});
let restClient: RestClient;
let signer: string;
let issuer: Identity;
let holder: Identity;
let assetParams: ReturnType<typeof createAssetParams>;
let assetId: string;

beforeAll(async () => {
factory = await TestFactory.create({ handles });
({ restClient } = factory);
issuer = factory.getSignerIdentity(handles[0]);
holder = factory.getSignerIdentity(handles[1]);

signer = issuer.signer;

assetParams = createAssetParams({
options: { processMode: ProcessMode.Submit, signer },
});
});

afterAll(async () => {
await factory.close();
});
afterAll(async () => {
await factory.close();
});

it('should create and fetch the Asset', async () => {
assetId = await restClient.assets.createAndGetAssetId(assetParams);
it('should create and fetch the Asset', async () => {
assetId = await restClient.assets.createAndGetAssetId(assetParams);

const asset = await restClient.assets.getAsset(assetId);
const asset = await restClient.assets.getAsset(assetId);

expect(asset).toMatchObject({
name: assetParams.name,
assetType: assetParams.assetType,
});
expect(asset).toMatchObject({
name: assetParams.name,
assetType: assetParams.assetType,
});

it('should transfer the asset to holder', async () => {
const transferToHolderTx = await restClient.settlements.createDirectInstruction(fungibleInstructionParams(assetId, issuer.did, holder.did, {
options: { processMode: ProcessMode.Submit, signer },
}));

// should have created an instruction
expect((transferToHolderTx as RestSuccessResult).instruction).toBeDefined();

const txData = await restClient.settlements.affirmInstruction(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(transferToHolderTx as any).instruction,
{
options: { processMode: ProcessMode.Submit, signer: holder.signer },
}
);

expect(txData).toMatchObject({
transactions: expect.arrayContaining([
{
transactionTag: 'settlement.affirmInstructionWithCount',
type: 'single',
...expectBasicTxInfo,
},
]),
});

const { results } = await restClient.assets.getAssetHolders(assetId);

expect(results.length).toEqual(2);
expect(results).toContainEqual(expect.objectContaining({
identity: issuer.did,
balance: '99990',
}));
expect(results).toContainEqual(expect.objectContaining({
identity: holder.did,
balance: '10',
}));
});

it('should transfer the asset to holder', async () => {
const transferToHolderTx = await restClient.settlements.createDirectInstruction(
fungibleInstructionParams(assetId, issuer.did, holder.did, {
options: { processMode: ProcessMode.Submit, signer },
})
);

// should have created an instruction
expect((transferToHolderTx as RestSuccessResult).instruction).toBeDefined();

const txData = await restClient.settlements.affirmInstruction(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(transferToHolderTx as any).instruction,
{
options: { processMode: ProcessMode.Submit, signer: holder.signer },
}
);

expect(txData).toMatchObject({
transactions: expect.arrayContaining([
{
transactionTag: 'settlement.affirmInstructionWithCount',
type: 'single',
...expectBasicTxInfo,
},
]),
});

it('should run controller transfer and return the asset back to the issuer', async () => {
const controllerTransferTx = await restClient.assets.controllerTransfer(assetId, controllerTransferParams({ did: holder.did, id: '0' }, 10, {
options: { processMode: ProcessMode.Submit, signer },
})) as RestSuccessResult;

expect(controllerTransferTx).toMatchObject({
transactions: expect.arrayContaining([
{
transactionTag: 'asset.controllerTransfer',
type: 'single',
...expectBasicTxInfo,
},
]),
});


const { results } = await restClient.assets.getAssetHolders(assetId);

expect(results.length).toEqual(1);
expect(results).toContainEqual(expect.objectContaining({
identity: expect.objectContaining({
did: issuer.did,
}),
balance: expect.objectContaining({
amount: '100000',
}),
}));
const { results } = await restClient.assets.getAssetHolders(assetId);

expect(results.length).toEqual(2);
expect(results).toContainEqual(
expect.objectContaining({
identity: issuer.did,
balance: '99990',
})
);
expect(results).toContainEqual(
expect.objectContaining({
identity: holder.did,
balance: '10',
})
);
});

it('should run controller transfer and return the asset back to the issuer', async () => {
const controllerTransferTx = (await restClient.assets.controllerTransfer(
assetId,
controllerTransferParams({ did: holder.did, id: '0' }, 10, {
options: { processMode: ProcessMode.Submit, signer },
})
)) as RestSuccessResult;

expect(controllerTransferTx).toMatchObject({
transactions: expect.arrayContaining([
{
transactionTag: 'asset.controllerTransfer',
type: 'single',
...expectBasicTxInfo,
},
]),
});

const { results } = await restClient.assets.getAssetHolders(assetId);

expect(results.length).toBeGreaterThan(1);
expect(results).toEqual(
expect.arrayContaining([
{
identity: issuer.did,
balance: '100000',
},
])
);
});
});
58 changes: 43 additions & 15 deletions tests/src/sdk/settlements/transferRestrictions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { BigNumber, Polymesh } from '@polymeshassociation/polymesh-sdk';
import { FungibleAsset } from '@polymeshassociation/polymesh-sdk/types';
import {
FungibleAsset,
StatType,
TransferRestrictionType,
} from '@polymeshassociation/polymesh-sdk/types';
import assert from 'node:assert';

import { wellKnown } from '~/consts';
Expand All @@ -23,39 +27,53 @@ export const transferRestrictions = async (sdk: Polymesh, asset: FungibleAsset):
Transfer Restrictions require their respective stats to be enabled. Enabling a stat will slightly increase gas fees for all transfers.
Statistics can also be enabled during creation by passing `initialStatistics` in the `sdk.assets.createAsset` call
*/
const addPercentageStatTx = await asset.transferRestrictions.percentage.enableStat();
const addPercentageStatTx = await asset.transferRestrictions.setStats({
stats: [
{
type: StatType.Balance,
},
],
});
await addPercentageStatTx.run();
assert(addPercentageStatTx.isSuccess);

const addPercentageRestrictionTx = await asset.transferRestrictions.percentage.addRestriction({
percentage: new BigNumber(10),
const addPercentageRestrictionTx = await asset.transferRestrictions.setRestrictions({
restrictions: [
{
type: TransferRestrictionType.Percentage,
percentage: new BigNumber(10),
},
],
});
await addPercentageRestrictionTx.run();
assert(addPercentageRestrictionTx.isSuccess);

// Multiple restrictions can be set at once. This overrides existing "percentage" claims
const setPercentageRestrictionsTx = await asset.transferRestrictions.percentage.setRestrictions({
const setPercentageRestrictionsTx = await asset.transferRestrictions.setRestrictions({
restrictions: [
{
type: TransferRestrictionType.Percentage,
percentage: new BigNumber(10),
// (optional) investors can be exempt from any restriction, specified by an Identity or DID string
exemptedIdentities: [identity],
// exemptedIdentities: [identity],
},
],
});
await setPercentageRestrictionsTx.run();
assert(setPercentageRestrictionsTx.isSuccess);

const { restrictions } = await asset.transferRestrictions.percentage.get();
const { restrictions } = await asset.transferRestrictions.getRestrictions();
assert(restrictions.length > 0, 'there should be a percentage restriction');

// All restrictions of a certain type can be removed
const removeRestrictionsTx = await asset.transferRestrictions.percentage.removeRestrictions();
// All restrictions can be removed
const removeRestrictionsTx = await asset.transferRestrictions.setRestrictions({
restrictions: [],
});
await removeRestrictionsTx.run();
assert(removeRestrictionsTx.isSuccess);

// Statistics should be disabled if no Transfer Restriction is using them
const disablePercentageStatTx = await asset.transferRestrictions.percentage.disableStat();
const disablePercentageStatTx = await asset.transferRestrictions.setStats({ stats: [] });
await disablePercentageStatTx.run();
assert(disablePercentageStatTx.isSuccess);

Expand All @@ -66,16 +84,26 @@ export const transferRestrictions = async (sdk: Polymesh, asset: FungibleAsset):
IMPORTANT! there is a potential "time of check, time of use" bug. Trading should be paused during the count stat creation process
*/
const count = await asset.investorCount();
const enableCountStatTx = await asset.transferRestrictions.count.enableStat({
count,
const enableCountStatTx = await asset.transferRestrictions.setStats({
stats: [
{
type: StatType.Count,
count,
},
],
});
await enableCountStatTx.run();
assert(enableCountStatTx.isSuccess);

// Create a restriction to limit the Asset to have at most 10 holders
const addCountRestrictionTx = await asset.transferRestrictions.count.addRestriction({
count: new BigNumber(10),
exemptedIdentities: [wellKnown.alice.did, identity],
const addCountRestrictionTx = await asset.transferRestrictions.setRestrictions({
restrictions: [
{
count: new BigNumber(10),
type: TransferRestrictionType.Count,
// exemptedIdentities: [wellKnown.alice.did, identity],
},
],
});
await addCountRestrictionTx.run();
assert(addCountRestrictionTx.isSuccess);
Expand Down
Loading