Skip to content

Commit 708bd30

Browse files
committed
feat: add shareholders.revokeKyc to the Security Token Entity
revokeKyc receives an array of shareholder addresses and forcibly expires their KYC
1 parent 08fb0cc commit 708bd30

File tree

9 files changed

+146
-11
lines changed

9 files changed

+146
-11
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"@0x/subproviders": "^3.0.2",
5050
"@babel/polyfill": "^7.0.0",
5151
"@babel/runtime": "^7.2.0",
52-
"@polymathnetwork/contract-wrappers": "2.0.0-beta.32",
52+
"@polymathnetwork/contract-wrappers": "2.0.0-beta.35",
5353
"bluebird": "^3.5.5",
5454
"ethereum-address": "^0.0.4",
5555
"json-stable-stringify": "^1.0.1",

src/Polymath.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ interface GetSecurityToken {
5454
}
5555

5656
export class Polymath {
57-
public networkId: number = -1;
58-
5957
public isUnsupported: boolean = false;
6058

6159
public isConnected: boolean = false;

src/entities/SecurityToken/Shareholders.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import {
22
ModuleName,
33
SecurityToken as SecurityTokenWrapper,
44
} from '@polymathnetwork/contract-wrappers';
5-
import { ShareholderDataEntry, DividendType, ErrorCode } from '../../types';
6-
import { ModifyShareholderData, CreateCheckpoint } from '../../procedures';
5+
import { ShareholderDataEntry, ErrorCode } from '../../types';
6+
import { ModifyShareholderData, CreateCheckpoint, RevokeKyc } from '../../procedures';
77
import { SubModule } from './SubModule';
88
import { Checkpoint } from '../Checkpoint';
99
import { PolymathError } from '../../PolymathError';
@@ -35,6 +35,22 @@ export class Shareholders extends SubModule {
3535
return procedure.prepare();
3636
};
3737

38+
/**
39+
* Revokes KYC for a group of shareholder addresses. Supplied addresses must have valid KYC
40+
*
41+
* @param shareholderAddresses array of shareholder addresses
42+
*/
43+
public revokeKyc = async (args: { shareholderAddresses: string[] }) => {
44+
const procedure = new RevokeKyc(
45+
{
46+
symbol: this.securityToken.symbol,
47+
...args,
48+
},
49+
this.context
50+
);
51+
return procedure.prepare();
52+
};
53+
3854
/**
3955
* Create a snapshot of the balances of every shareholder at the current date
4056
*/

src/procedures/CreateSecurityToken.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export class CreateSecurityToken extends Procedure<
6464
});
6565
}
6666

67-
const [usdFee, polyFee] = await securityTokenRegistry.getFees({ feeType: FeeType.stLaunchFee });
67+
const [usdFee, polyFee] = await securityTokenRegistry.getFees({ feeType: FeeType.StLaunchFee });
6868

6969
await this.addProcedure(ApproveErc20)({
7070
amount: polyFee,

src/procedures/ReserveSecurityToken.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export class ReserveSecurityToken extends Procedure<
5252
}
5353

5454
const [usdFee, polyFee] = await securityTokenRegistry.getFees({
55-
feeType: FeeType.tickerRegFee,
55+
feeType: FeeType.TickerRegFee,
5656
});
5757
await addProcedure(ApproveErc20)({
5858
amount: polyFee,

src/procedures/RevokeKyc.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { ModuleName } from '@polymathnetwork/contract-wrappers';
2+
import { difference } from 'lodash';
3+
import { Procedure } from './Procedure';
4+
import { ProcedureType, PolyTransactionTag, ErrorCode, RevokeKycProcedureArgs } from '../types';
5+
import { PolymathError } from '../PolymathError';
6+
import { Shareholder, SecurityToken } from '../entities';
7+
8+
export class RevokeKyc extends Procedure<RevokeKycProcedureArgs, Shareholder[]> {
9+
public type = ProcedureType.CreateErc20DividendDistribution;
10+
11+
public async prepareTransactions() {
12+
const { symbol, shareholderAddresses } = this.args;
13+
const { contractWrappers, factories } = this.context;
14+
15+
if (shareholderAddresses.length === 0) {
16+
throw new PolymathError({
17+
code: ErrorCode.ProcedureValidationError,
18+
message: 'You must provide at least one shareholder address to revoke KYC for',
19+
});
20+
}
21+
22+
try {
23+
await contractWrappers.tokenFactory.getSecurityTokenInstanceFromTicker(symbol);
24+
} catch (err) {
25+
throw new PolymathError({
26+
code: ErrorCode.ProcedureValidationError,
27+
message: `There is no Security Token with symbol ${symbol}`,
28+
});
29+
}
30+
31+
const securityToken = await factories.securityTokenFactory.fetch(
32+
SecurityToken.generateId({ symbol })
33+
);
34+
35+
const shareholders = await securityToken.shareholders.getShareholders();
36+
const now = new Date();
37+
38+
const currentNonExpiredShareholderAddresses = shareholders
39+
.filter(({ kycExpiry }) => kycExpiry > now)
40+
.map(({ address }) => address);
41+
const diff = difference(shareholderAddresses, currentNonExpiredShareholderAddresses);
42+
43+
if (diff.length) {
44+
throw new PolymathError({
45+
code: ErrorCode.ProcedureValidationError,
46+
message: `KYC for "${diff.join(', ')}" has already expired`,
47+
});
48+
}
49+
50+
const gtmModule = (await contractWrappers.getAttachedModules(
51+
{
52+
moduleName: ModuleName.GeneralTransferManager,
53+
symbol,
54+
},
55+
{ unarchived: true }
56+
))[0];
57+
58+
if (!gtmModule) {
59+
throw new PolymathError({
60+
code: ErrorCode.FatalError,
61+
message: `General Transfer Manager for token "${symbol}" isn't enabled. Please report this issue to the Polymath team`,
62+
});
63+
}
64+
65+
const canSendAfter: Date[] = [];
66+
const canReceiveAfter: Date[] = [];
67+
const expiryTime: Date[] = [];
68+
69+
shareholderAddresses.forEach(_address => {
70+
canSendAfter.push(new Date(0));
71+
canReceiveAfter.push(new Date(0));
72+
expiryTime.push(new Date(0));
73+
});
74+
75+
const securityTokenId = SecurityToken.generateId({ symbol });
76+
77+
let revokedShareholders;
78+
79+
revokedShareholders = await this.addTransaction(gtmModule.modifyKYCDataMulti, {
80+
tag: PolyTransactionTag.ModifyKycDataMulti,
81+
resolver: async () => {
82+
const refreshingShareholders = shareholderAddresses.map(shareholder => {
83+
return factories.shareholderFactory.refresh(
84+
Shareholder.generateId({
85+
securityTokenId,
86+
address: shareholder,
87+
})
88+
);
89+
});
90+
91+
await Promise.all(refreshingShareholders);
92+
93+
const fetchingShareholders = shareholderAddresses.map(shareholder => {
94+
return factories.shareholderFactory.fetch(
95+
Shareholder.generateId({
96+
securityTokenId,
97+
address: shareholder,
98+
})
99+
);
100+
});
101+
102+
return Promise.all(fetchingShareholders);
103+
},
104+
})({
105+
investors: shareholderAddresses,
106+
canSendAfter,
107+
canReceiveAfter,
108+
expiryTime,
109+
});
110+
111+
return revokedShareholders;
112+
}
113+
}

src/procedures/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ export { SetController } from './SetController';
1919
export { LaunchCappedSto } from './LaunchCappedSto';
2020
export { LaunchUsdTieredSto } from './LaunchUsdTieredSto';
2121
export { ModifyShareholderData } from './ModifyShareholderData';
22+
export { RevokeKyc } from './RevokeKyc';

src/types/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export enum ProcedureType {
9797
PauseSto = 'PauseSto',
9898
SetController = 'SetController',
9999
ModifyShareholderData = 'ModifyShareholderData',
100+
RevokeKyc = 'RevokeKyc',
100101
}
101102

102103
export enum PolyTransactionTag {
@@ -349,6 +350,11 @@ export interface ModifyShareholderDataProcedureArgs {
349350
shareholderData: ShareholderDataEntry[];
350351
}
351352

353+
export interface RevokeKycProcedureArgs {
354+
symbol: string;
355+
shareholderAddresses: string[];
356+
}
357+
352358
export interface ProcedureArguments {
353359
[ProcedureType.ApproveErc20]: ApproveErc20ProcedureArgs;
354360
[ProcedureType.TransferErc20]: TransferErc20ProcedureArgs;
@@ -370,6 +376,7 @@ export interface ProcedureArguments {
370376
[ProcedureType.SetController]: SetControllerProcedureArgs;
371377
[ProcedureType.ChangeDelegatePermission]: ChangeDelegatePermissionProcedureArgs;
372378
[ProcedureType.ModifyShareholderData]: ModifyShareholderDataProcedureArgs;
379+
[ProcedureType.RevokeKyc]: RevokeKycProcedureArgs;
373380
[ProcedureType.UnnamedProcedure]: {};
374381
}
375382

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,10 +1337,10 @@
13371337
resolved "https://registry.yarnpkg.com/@polymathnetwork/contract-artifacts/-/contract-artifacts-3.0.0-beta.5.tgz#b3bf6c7f40eb036402237d08d98f4f11cc230c87"
13381338
integrity sha512-YS6VXPqEgv69SEUJHArNLrN93psfPZSRFBxX0TnLPiNC/9wsvdDUXhE0a3RhokEMjYBmD4Yi+CDB71Faxg3jpQ==
13391339

1340-
"@polymathnetwork/contract-wrappers@2.0.0-beta.32":
1341-
version "2.0.0-beta.32"
1342-
resolved "https://registry.yarnpkg.com/@polymathnetwork/contract-wrappers/-/contract-wrappers-2.0.0-beta.32.tgz#0a7ec1570e538702c551e0ad5df6b6689277e717"
1343-
integrity sha512-gqKNfna7RgMH7DOrfxnWW1GwspAnm9tIRsjpAZ3CLP/02PmYdK8pZP2XN19SdhmQ6JKWAGA3zi4cqfT5dw4a4w==
1340+
"@polymathnetwork/contract-wrappers@2.0.0-beta.35":
1341+
version "2.0.0-beta.35"
1342+
resolved "https://registry.yarnpkg.com/@polymathnetwork/contract-wrappers/-/contract-wrappers-2.0.0-beta.35.tgz#fe5e8604e86570099e39476f746ce48233921d19"
1343+
integrity sha512-OgoSjqmnWM8F6OUFzWLyFvGrWtizZogewTL3uwRLkIBoXePrj14esm/3O+Y3Kho2ha4+YuOjoLpdsy0DmTqWtw==
13441344
dependencies:
13451345
"@0x/assert" "^2.1.0"
13461346
"@0x/json-schemas" "^3.0.11"

0 commit comments

Comments
 (0)