Skip to content

Commit

Permalink
feat: refacor on issueTokens procedure. Task involved: MSDK-261
Browse files Browse the repository at this point in the history
  • Loading branch information
shuffledex committed Sep 23, 2020
1 parent 5b77649 commit eae437b
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 164 deletions.
8 changes: 4 additions & 4 deletions src/api/entities/SecurityToken/Issuance.ts
@@ -1,18 +1,18 @@
import { Namespace, SecurityToken } from '~/api/entities';
import { issueTokens } from '~/api/procedures';
import { TransactionQueue } from '~/base';
import { IssuanceData } from '~/types';
import { IssuanceAmount } from '~/types';

/**
* Handles all Security Token Issuance related functionality
*/
export class Issuance extends Namespace<SecurityToken> {
/**
* Issue a certain amount of tokens to one or multiple Identities. The receiving Identities must comply with any receiver rules set on the token
* Issue a certain amount of tokens to treasury account
*
* @param args.issuanceData - array that specifies who to issue tokens to and which amounts
* @param args.issuanceAmount - amount of tokens to be issued to treasury
*/
public issue(args: { issuanceData: IssuanceData[] }): Promise<TransactionQueue<SecurityToken>> {
public issue(args: { issuanceAmount: IssuanceAmount }): Promise<TransactionQueue<SecurityToken>> {
const {
parent: { ticker },
context,
Expand Down
9 changes: 3 additions & 6 deletions src/api/entities/SecurityToken/__tests__/Issuance.ts
Expand Up @@ -39,12 +39,9 @@ describe('Issuance class', () => {
const issuance = new Issuance(token, context);

const args = {
issuanceData: [
{
identity: 'someDid',
amount: new BigNumber(100),
},
],
issuanceAmount: {
amount: new BigNumber(100),
},
};

const expectedQueue = ('someQueue' as unknown) as TransactionQueue<SecurityToken>;
Expand Down
4 changes: 2 additions & 2 deletions src/api/entities/SecurityToken/__tests__/index.ts
Expand Up @@ -87,7 +87,7 @@ describe('SecurityToken class', () => {
asset_type: dsMockUtils.createMockAssetType(assetType),
divisible: dsMockUtils.createMockBool(isDivisible),
total_supply: dsMockUtils.createMockBalance(totalSupply),
treasury_did: dsMockUtils.createMockOption(
primary_issuance_agent: dsMockUtils.createMockOption(
dsMockUtils.createMockIdentityId(treasuryIdentity)
),
/* eslint-enable @typescript-eslint/camelcase */
Expand Down Expand Up @@ -115,7 +115,7 @@ describe('SecurityToken class', () => {
test('should allow subscription', async () => {
const unsubCallback = 'unsubCallBack';
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/camelcase
(rawToken as any).treasury_did = dsMockUtils.createMockOption();
(rawToken as any).primary_issuance_agent = dsMockUtils.createMockOption();

dsMockUtils.createQueryStub('asset', 'tokens').callsFake(async (_, cbFunc) => {
cbFunc(rawToken);
Expand Down
6 changes: 3 additions & 3 deletions src/api/entities/SecurityToken/index.ts
Expand Up @@ -155,15 +155,15 @@ export class SecurityToken extends Entity<UniqueIdentifiers> {
divisible,
owner_did,
asset_type,
treasury_did,
primary_issuance_agent,
}: MeshSecurityToken): SecurityTokenDetails => ({
assetType: assetTypeToString(asset_type),
isDivisible: boolToBoolean(divisible),
name: assetNameToString(name),
owner: new Identity({ did: identityIdToString(owner_did) }, context),
totalSupply: balanceToBigNumber(total_supply),
treasuryIdentity: treasury_did.isSome
? new Identity({ did: identityIdToString(treasury_did.unwrap()) }, context)
treasuryIdentity: primary_issuance_agent.isSome
? new Identity({ did: identityIdToString(primary_issuance_agent.unwrap()) }, context)
: null,
});
/* eslint-enable @typescript-eslint/camelcase */
Expand Down
114 changes: 36 additions & 78 deletions src/api/procedures/__tests__/issueTokens.ts
@@ -1,5 +1,6 @@
import { Balance } from '@polkadot/types/interfaces';
import BigNumber from 'bignumber.js';
import { IssueAssetItem, Ticker } from 'polymesh-types/types';
import { Ticker } from 'polymesh-types/types';
import sinon from 'sinon';

import { SecurityToken } from '~/api/entities';
Expand All @@ -19,22 +20,29 @@ jest.mock(
describe('issueTokens procedure', () => {
let mockContext: Mocked<Context>;
let stringToTickerStub: sinon.SinonStub<[string, Context], Ticker>;
let numberToBalance: sinon.SinonStub<[number | BigNumber, Context], Balance>;
let ticker: string;
let rawTicker: Ticker;
let amount: BigNumber;
let rawAmount: Balance;
let addTransactionStub: sinon.SinonStub;

beforeAll(() => {
dsMockUtils.initMocks();
procedureMockUtils.initMocks();
entityMockUtils.initMocks();
stringToTickerStub = sinon.stub(utilsModule, 'stringToTicker');
numberToBalance = sinon.stub(utilsModule, 'numberToBalance');
ticker = 'someTicker';
rawTicker = dsMockUtils.createMockTicker(ticker);
amount = new BigNumber(100);
rawAmount = dsMockUtils.createMockBalance(amount.toNumber());
});

beforeEach(() => {
mockContext = dsMockUtils.getContextInstance();
stringToTickerStub.withArgs(ticker, mockContext).returns(rawTicker);
numberToBalance.withArgs(amount, mockContext).returns(rawAmount);
addTransactionStub = procedureMockUtils.getAddTransactionStub();
});

Expand All @@ -50,18 +58,11 @@ describe('issueTokens procedure', () => {
dsMockUtils.cleanup();
});

test('should throw an error if security token is divisible and at least one amount exceeds six decimals', () => {
test('should throw an error if security token is divisible and the amount exceeds six decimals', () => {
const args = {
issuanceData: [
{
identity: 'someDid',
amount: new BigNumber(100),
},
{
identity: 'anotherDid',
amount: new BigNumber(50.1234567),
},
],
issuanceAmount: {
amount,
},
ticker,
};

Expand All @@ -76,40 +77,31 @@ describe('issueTokens procedure', () => {
const proc = procedureMockUtils.getInstance<Params, SecurityToken>(mockContext);

return expect(prepareIssueTokens.call(proc, args)).rejects.toThrow(
`Issuance amounts cannot have more than ${MAX_DECIMALS} decimals`
`Issuance amount cannot have more than ${MAX_DECIMALS} decimals`
);
});

test('should throw an error if security token is not divisible and at least one amount has decimals', () => {
test('should throw an error if security token is not divisible and the amount has decimals', () => {
const args = {
issuanceData: [
{
identity: 'someDid',
amount: new BigNumber(100),
},
{
identity: 'anotherDid',
amount: new BigNumber(50.1),
},
],
issuanceAmount: {
amount,
},
ticker,
};

const proc = procedureMockUtils.getInstance<Params, SecurityToken>(mockContext);

return expect(prepareIssueTokens.call(proc, args)).rejects.toThrow(
'Cannot issue decimal amounts of an indivisible token'
'Cannot issue decimal amount of an indivisible token'
);
});

test('should throw an error if token supply is bigger than the limit total supply', async () => {
const args = {
issuanceData: [
{
identity: 'someDid',
amount: new BigNumber(100),
},
],
issuanceAmount: {
amount,
},

ticker,
};

Expand Down Expand Up @@ -145,12 +137,10 @@ describe('issueTokens procedure', () => {
test('should throw an error if canMint returns a status different from Success', async () => {
const transferStatus = TransferStatus.Failure;
const args = {
issuanceData: [
{
identity: 'someDid',
amount: new BigNumber(100),
},
],
issuanceAmount: {
amount,
},

ticker,
};

Expand All @@ -170,59 +160,27 @@ describe('issueTokens procedure', () => {
error = err;
}

expect(error.message).toBe("You can't issue tokens to some of the supplied Identities");
expect(error.message).toBe("You can't issue tokens to treasury account");
expect(error.data).toMatchObject({
failed: [{ did: args.issuanceData[0].identity, transferStatus }],
failed: [{ transferStatus }],
});
});

test('should add a batch issue transaction to the queue', async () => {
test('should add a issue transaction to the queue', async () => {
const args = {
issuanceData: [
{
identity: 'someDid',
amount: new BigNumber(100),
},
{
identity: 'otherDid',
amount: new BigNumber(200),
},
],
issuanceAmount: {
amount,
},

ticker,
};

const items: IssueAssetItem[] = [];

const issuanceDataToIssueAssetItemStub = sinon.stub(
utilsModule,
'issuanceDataToIssueAssetItem'
);

args.issuanceData.forEach(({ identity, amount }) => {
const identityId = dsMockUtils.createMockIdentityId(`${identity}Identity`);
const balance = dsMockUtils.createMockBalance(amount.toNumber());
const item = dsMockUtils.createMockIssueAssetItem({
// eslint-disable-next-line @typescript-eslint/camelcase
identity_did: identityId,
value: balance,
});
items.push(item);

issuanceDataToIssueAssetItemStub.withArgs({ identity, amount }, mockContext).returns(item);
});

const transaction = dsMockUtils.createTxStub('asset', 'batchIssue');
const transaction = dsMockUtils.createTxStub('asset', 'issue');
const proc = procedureMockUtils.getInstance<Params, SecurityToken>(mockContext);

const result = await prepareIssueTokens.call(proc, args);

sinon.assert.calledWith(
addTransactionStub,
transaction,
{ batchSize: args.issuanceData.length },
items,
rawTicker
);
sinon.assert.calledWith(addTransactionStub, transaction, {}, rawTicker, rawAmount);
expect(result.ticker).toBe(ticker);
});
});
Expand Down

0 comments on commit eae437b

Please sign in to comment.