Skip to content

Commit

Permalink
feat(transfer restrictions): add logic to fetch transfer restrictions
Browse files Browse the repository at this point in the history
  • Loading branch information
monitz87 committed Jan 18, 2021
1 parent f97a908 commit 471e97f
Show file tree
Hide file tree
Showing 12 changed files with 406 additions and 184 deletions.
32 changes: 13 additions & 19 deletions src/api/entities/AuthorizationRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
Context,
Entity,
Identity,
TransactionQueue,
} from '~/internal';
import { Authorization, AuthorizationType, Signer } from '~/types';
import { ProcedureMethod } from '~/types/internal';
Expand Down Expand Up @@ -106,6 +105,18 @@ export class AuthorizationRequest extends Entity<UniqueIdentifiers> {

return [consumeAuthorizationRequests, { authRequests: [this], accept: true }];
}, context);

this.remove = createProcedureMethod<
void,
ConsumeAuthorizationRequestsParams | ConsumeJoinIdentityAuthorizationParams,
void
>(() => {
if (this.data.type === AuthorizationType.JoinIdentity) {
return [consumeJoinIdentityAuthorization, { authRequest: this, accept: false }];
}

return [consumeAuthorizationRequests, { authRequests: [this], accept: false }];
}, context);
}

/**
Expand All @@ -119,24 +130,7 @@ export class AuthorizationRequest extends Entity<UniqueIdentifiers> {
* - If you are the request issuer, this will cancel the authorization
* - If you are the request target, this will reject the authorization
*/
public remove(): Promise<TransactionQueue> {
const {
context,
data: { type },
} = this;

if (type === AuthorizationType.JoinIdentity) {
return consumeJoinIdentityAuthorization.prepare(
{ authRequest: this, accept: false },
context
);
}

return consumeAuthorizationRequests.prepare(
{ authRequests: [this], accept: false },
this.context
);
}
public remove: ProcedureMethod<void, void>;

/**
* Returns whether the Authorization Request has expired
Expand Down
43 changes: 15 additions & 28 deletions src/api/entities/SecurityToken/TransferRestrictions/Count.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,13 @@
import {
AddCountTransferRestrictionParams,
addTransferRestriction,
AddTransferRestrictionParams,
Context,
Namespace,
SecurityToken,
} from '~/internal';
import { TransferRestrictionBase } from '~/api/entities/SecurityToken/TransferRestrictions/TransferRestrictionBase';
import { AddCountTransferRestrictionParams } from '~/internal';
import { ActiveTransferRestrictions, CountTransferRestriction } from '~/types';
import { ProcedureMethod, TransferRestrictionType } from '~/types/internal';
import { createProcedureMethod } from '~/utils/internal';

/**
* Handles all Count Transfer Restriction related functionality
*/
export class Count extends Namespace<SecurityToken> {
/**
* @hidden
*/
constructor(parent: SecurityToken, context: Context) {
super(parent, context);

const { ticker } = parent;

this.addRestriction = createProcedureMethod<
Omit<AddCountTransferRestrictionParams, 'type'>,
AddTransferRestrictionParams,
number
>(
args => [addTransferRestriction, { ...args, type: TransferRestrictionType.Count, ticker }],
context
);
}
export class Count extends TransferRestrictionBase<TransferRestrictionType.Count> {
protected type = TransferRestrictionType.Count as const;

/**
* Add a Count Transfer Restriction to this Security Token
Expand All @@ -39,5 +17,14 @@ export class Count extends Namespace<SecurityToken> {
*
* @note the result is the total amount of restrictions after the procedure has run
*/
public addRestriction: ProcedureMethod<Omit<AddCountTransferRestrictionParams, 'type'>, number>;
public addRestriction!: ProcedureMethod<Omit<AddCountTransferRestrictionParams, 'type'>, number>;

/**
* Retrieve all active Count Transfer Restrictions
*
* @note there is a maximum number of restrictions allowed accross all types.
* The `availableSlots` property of the result represents how many more restrictions can be added
* before reaching that limit
*/
public get!: () => Promise<ActiveTransferRestrictions<CountTransferRestriction>>;
}
50 changes: 17 additions & 33 deletions src/api/entities/SecurityToken/TransferRestrictions/Percentage.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,33 @@
import {
AddPercentageTransferRestrictionParams,
addTransferRestriction,
AddTransferRestrictionParams,
Context,
Namespace,
SecurityToken,
} from '~/internal';
import { TransferRestrictionBase } from '~/api/entities/SecurityToken/TransferRestrictions/TransferRestrictionBase';
import { AddPercentageTransferRestrictionParams } from '~/internal';
import { ActiveTransferRestrictions, PercentageTransferRestriction } from '~/types';
import { ProcedureMethod, TransferRestrictionType } from '~/types/internal';
import { createProcedureMethod } from '~/utils/internal';

/**
* Handles all Percentage Transfer Restriction related functionality
*/
export class Percentage extends Namespace<SecurityToken> {
/**
* @hidden
*/
constructor(parent: SecurityToken, context: Context) {
super(parent, context);

const { ticker } = parent;

this.addRestriction = createProcedureMethod<
Omit<AddPercentageTransferRestrictionParams, 'type'>,
AddTransferRestrictionParams,
number
>(
args => [
addTransferRestriction,
{ ...args, type: TransferRestrictionType.Percentage, ticker },
],
context
);
}
export class Percentage extends TransferRestrictionBase<TransferRestrictionType.Percentage> {
protected type = TransferRestrictionType.Percentage as const;

/**
* Add a Percentage Transfer Restriction to this Security Token
*
* @param args.percentage - limit on the percentage of the total supply of this Security Token that a single (unique) investor can hold at once
* @param args.percentage - limit on the proportion of the total supply of this Security Token that can be held by a single investor at once
* @param args.exempted - array of Scope IDs that are exempted from the Restriction
*
* * @note the result is the total amount of restrictions after the procedure has run
* @note the result is the total amount of restrictions after the procedure has run
*/
public addRestriction: ProcedureMethod<
public addRestriction!: ProcedureMethod<
Omit<AddPercentageTransferRestrictionParams, 'type'>,
number
>;

/**
* Retrieve all active Percentage Transfer Restrictions
*
* @note there is a maximum number of restrictions allowed accross all types.
* The `availableSlots` property of the result represents how many more restrictions can be added
* before reaching that limit
*/
public get!: () => Promise<ActiveTransferRestrictions<PercentageTransferRestriction>>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { AddPercentageTransferRestrictionParams } from '~/api/procedures/addTransferRestriction';
import {
AddCountTransferRestrictionParams,
addTransferRestriction,
AddTransferRestrictionParams,
Context,
Namespace,
SecurityToken,
} from '~/internal';
import {
ActiveTransferRestrictions,
CountTransferRestriction,
PercentageTransferRestriction,
} from '~/types';
import { ProcedureMethod, TransferRestrictionType } from '~/types/internal';
import { MAX_TRANSFER_MANAGERS } from '~/utils/constants';
import {
scopeIdToString,
stringToTicker,
transferManagerToTransferRestriction,
} from '~/utils/conversion';
import { createProcedureMethod } from '~/utils/internal';

type AddRestrictionParams<T> = Omit<
T extends TransferRestrictionType.Count
? AddCountTransferRestrictionParams
: AddPercentageTransferRestrictionParams,
'type'
>;

type GetReturnType<T> = ActiveTransferRestrictions<
T extends TransferRestrictionType.Count ? CountTransferRestriction : PercentageTransferRestriction
>;

/**
* Base class for managing Transfer Restrictions
*/
export abstract class TransferRestrictionBase<
T extends TransferRestrictionType
> extends Namespace<SecurityToken> {
protected abstract type: T;

/**
* @hidden
*/
constructor(parent: SecurityToken, context: Context) {
super(parent, context);

const { ticker } = parent;

this.addRestriction = createProcedureMethod<
AddRestrictionParams<T>,
AddTransferRestrictionParams,
number
>(
args => [
addTransferRestriction,
({ ...args, type: this.type, ticker } as unknown) as AddTransferRestrictionParams,
],
context
);
}

/**
* Add a Transfer Restriction of the corresponding type to this Security Token
*
* @param args.exempted - array of Scope IDs that are exempted from the Restriction
*
* @note the result is the total amount of restrictions after the procedure has run
*/
public addRestriction: ProcedureMethod<AddRestrictionParams<T>, number>;

/**
* Retrieve all active Transfer Restrictions of the corresponding type
*
* @note there is a maximum number of restrictions allowed accross all types.
* The `availableSlots` property of the result represents how many more restrictions can be added
* before reaching that limit
*/
public async get(): Promise<GetReturnType<T>> {
const {
parent: { ticker },
context: {
polymeshApi: {
query: { statistics },
},
},
context,
} = this;

const rawTicker = stringToTicker(ticker, context);
const activeTms = await statistics.activeTransferManagers(rawTicker);
const filteredTms = activeTms.filter(tm => {
if (this.type === TransferRestrictionType.Count) {
return tm.isCountTransferManager;
}

return tm.isPercentageTransferManager;
});

const rawExemptedLists = await Promise.all(
filteredTms.map(tm => statistics.exemptEntities.entries([rawTicker, tm]))
);

const restrictions = rawExemptedLists.map((list, index) => {
const exempted = list.map(([{ args: [, scopeId] }]) => scopeIdToString(scopeId));
const { value } = transferManagerToTransferRestriction(filteredTms[index]);
let restriction;

if (this.type === TransferRestrictionType.Count) {
restriction = {
count: value,
};
} else {
restriction = {
percentage: value,
};
}
return {
...restriction,
exempted,
};
});

return {
restrictions,
availableSlots: MAX_TRANSFER_MANAGERS - activeTms.length,
} as GetReturnType<T>;
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
import BigNumber from 'bignumber.js';
import sinon from 'sinon';

import {
AddCountTransferRestrictionParams,
addTransferRestriction,
Namespace,
TransactionQueue,
} from '~/internal';
import { Namespace } from '~/internal';
import { dsMockUtils, entityMockUtils } from '~/testUtils/mocks';
import { TransferRestrictionType } from '~/types/internal';

import { Count } from '../Count';

Expand All @@ -34,34 +25,4 @@ describe('Count class', () => {
test('should extend namespace', () => {
expect(Count.prototype instanceof Namespace).toBe(true);
});

describe('method: addRestriction', () => {
afterAll(() => {
sinon.restore();
});

test('should prepare the procedure with the correct arguments and context, and return the resulting transaction queue', async () => {
const context = dsMockUtils.getContextInstance();
const token = entityMockUtils.getSecurityTokenInstance();
const count = new Count(token, context);

const args: Omit<AddCountTransferRestrictionParams, 'type'> = {
count: new BigNumber(3),
exempted: ['someScopeId'],
};

const expectedQueue = ('someQueue' as unknown) as TransactionQueue<number>;

sinon
.stub(addTransferRestriction, 'prepare')
.withArgs({ ticker: token.ticker, ...args, type: TransferRestrictionType.Count }, context)
.resolves(expectedQueue);

const queue = await count.addRestriction({
...args,
});

expect(queue).toBe(expectedQueue);
});
});
});

0 comments on commit 471e97f

Please sign in to comment.