Skip to content
This repository was archived by the owner on Jul 6, 2022. It is now read-only.

Commit 92d394c

Browse files
committed
feat: 🎸 add vesting escrow wallet example
add VEW example and fix some issues discovered in the process
1 parent 1199cdc commit 92d394c

File tree

8 files changed

+250
-26
lines changed

8 files changed

+250
-26
lines changed

‎examples/percentageTransferManager.ts‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { RedundantSubprovider, RPCSubprovider, Web3ProviderEngine } from '@0x/subproviders';
2+
import { BigNumber } from '@polymathnetwork/abi-wrappers';
23
import ModuleFactoryWrapper from '../src/contract_wrappers/modules/module_factory_wrapper';
34
import { ApiConstructorParams, PolymathAPI } from '../src/PolymathAPI';
45
import { bytes32ToString } from '../src/utils/convert';
56
import { ModuleName, ModuleType } from '../src';
6-
import { BigNumber } from '@polymathnetwork/abi-wrappers';
77

88
// This file acts as a valid sandbox for using a percentage restriction transfer manager module on an unlocked node (like ganache)
99
window.addEventListener('load', async () => {
@@ -40,11 +40,7 @@ window.addEventListener('load', async () => {
4040
names.push(instanceFactory.name());
4141
});
4242
const resultNames = await Promise.all(names);
43-
44-
const finalNames = resultNames.map(name => {
45-
return bytes32ToString(name);
46-
});
47-
const index = finalNames.indexOf(moduleStringName);
43+
const index = resultNames.indexOf(moduleStringName);
4844

4945
// Call to add module
5046
await tickerSecurityTokenInstance.addModule({
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import { RedundantSubprovider, RPCSubprovider, Web3ProviderEngine } from '@0x/subproviders';
2+
import { BigNumber } from '@polymathnetwork/abi-wrappers';
3+
import { ApiConstructorParams, PolymathAPI } from '../src/PolymathAPI';
4+
import { ModuleName, ModuleType } from '../src';
5+
import ModuleFactoryWrapper from '../src/contract_wrappers/modules/module_factory_wrapper';
6+
7+
// This file acts as a valid sandbox.ts file in root directory for adding a new Vesting Escrow Wallet module
8+
9+
window.addEventListener('load', async () => {
10+
// Setup the redundant provider
11+
const providerEngine = new Web3ProviderEngine();
12+
providerEngine.addProvider(new RedundantSubprovider([new RPCSubprovider('http://127.0.0.1:8545')]));
13+
providerEngine.start();
14+
const params: ApiConstructorParams = {
15+
provider: providerEngine,
16+
polymathRegistryAddress: '<Deployed Polymath Registry Address>',
17+
};
18+
19+
// Instantiate the API
20+
const polymathAPI = new PolymathAPI(params);
21+
22+
// Get some poly tokens in your account and the security token
23+
const myAddress = await polymathAPI.getAccount();
24+
await polymathAPI.getPolyTokens({ amount: new BigNumber(1000000), address: myAddress });
25+
26+
// Prompt to setup your ticker and token name
27+
const ticker = prompt('Ticker', '');
28+
const tokenName = prompt('Token Name', '');
29+
30+
// Double check available
31+
await polymathAPI.securityTokenRegistry.isTickerAvailable({
32+
ticker: ticker!,
33+
});
34+
35+
// Get the ticker fee and approve the security token registry to spend
36+
const tickerFee = await polymathAPI.securityTokenRegistry.getTickerRegistrationFee();
37+
await polymathAPI.polyToken.approve({
38+
spender: await polymathAPI.securityTokenRegistry.address(),
39+
value: tickerFee,
40+
});
41+
42+
// Register a ticker
43+
await polymathAPI.securityTokenRegistry.registerTicker({
44+
ticker: ticker!,
45+
tokenName: tokenName!,
46+
});
47+
48+
// Get the st launch fee and approve the security token registry to spend
49+
const securityTokenLaunchFee = await polymathAPI.securityTokenRegistry.getSecurityTokenLaunchFee();
50+
await polymathAPI.polyToken.approve({
51+
spender: await polymathAPI.securityTokenRegistry.address(),
52+
value: securityTokenLaunchFee,
53+
});
54+
55+
// Generate a security token
56+
await polymathAPI.securityTokenRegistry.generateNewSecurityToken({
57+
name: tokenName!,
58+
ticker: ticker!,
59+
tokenDetails: 'http://',
60+
divisible: false,
61+
treasuryWallet: myAddress,
62+
protocolVersion: '0',
63+
});
64+
65+
// Create a Security Token Instance
66+
const tickerSecurityTokenInstance = await polymathAPI.tokenFactory.getSecurityTokenInstanceFromTicker(ticker!);
67+
console.log('ST address:', await tickerSecurityTokenInstance.address());
68+
69+
// Get permission manager factory address
70+
const moduleStringName = 'VestingEscrowWallet';
71+
const moduleName = ModuleName.VestingEscrowWallet;
72+
73+
const modules = await polymathAPI.moduleRegistry.getModulesByType({
74+
moduleType: ModuleType.Wallet,
75+
});
76+
77+
const instances: Promise<ModuleFactoryWrapper>[] = [];
78+
modules.map(address => {
79+
instances.push(polymathAPI.moduleFactory.getModuleFactory(address));
80+
});
81+
const resultInstances = await Promise.all(instances);
82+
83+
const names: Promise<string>[] = [];
84+
resultInstances.map(instanceFactory => {
85+
names.push(instanceFactory.name());
86+
});
87+
const resultNames = await Promise.all(names);
88+
const index = resultNames.indexOf(moduleStringName);
89+
90+
const factory = await polymathAPI.moduleFactory.getModuleFactory(modules[index]);
91+
const setupCost = await factory.setupCostInPoly();
92+
93+
// Call to add count transfer manager module
94+
await tickerSecurityTokenInstance.addModule({
95+
moduleName,
96+
address: modules[index],
97+
maxCost: setupCost,
98+
budget: setupCost,
99+
archived: false,
100+
data: {
101+
treasuryWallet: '0x2320351c4670a19C3DD05789d2648DD129A14669',
102+
},
103+
});
104+
105+
const VEWAddress = (await tickerSecurityTokenInstance.getModulesByName({
106+
moduleName: ModuleName.VestingEscrowWallet,
107+
}))[0];
108+
const VEW = await polymathAPI.moduleFactory.getModuleInstance({
109+
name: ModuleName.VestingEscrowWallet,
110+
address: VEWAddress,
111+
});
112+
113+
const VEWModuleAddress = await VEW.address();
114+
const numberOfTokens = 500;
115+
const TEMPLATE_NAME = 'Test Template';
116+
const beneficiary = '0x72aF7849ffc7753B5ccEA5cb80F97e9Aeaf7d999';
117+
118+
const generalTMAddress = (await tickerSecurityTokenInstance.getModulesByName({
119+
moduleName: ModuleName.GeneralTransferManager,
120+
}))[0];
121+
122+
const generalTM = await polymathAPI.moduleFactory.getModuleInstance({
123+
name: ModuleName.GeneralTransferManager,
124+
address: generalTMAddress,
125+
});
126+
127+
// Add owner address in the whitelist to allow mint tokens
128+
await generalTM.modifyKYCData({
129+
investor: myAddress,
130+
canSendAfter: new Date(2019, 7),
131+
canReceiveAfter: new Date(2019, 7),
132+
expiryTime: new Date(2020, 1),
133+
txData: {
134+
from: await polymathAPI.getAccount(),
135+
},
136+
});
137+
138+
// Mint yourself some tokens and make some transfers
139+
await tickerSecurityTokenInstance.issue({
140+
investor: myAddress,
141+
value: new BigNumber(100000),
142+
});
143+
144+
// Add VEW module in the whitelist (remove this on 3.1 release)
145+
await generalTM.modifyKYCData({
146+
investor: VEWModuleAddress,
147+
canSendAfter: new Date(2019, 7),
148+
canReceiveAfter: new Date(2019, 7),
149+
expiryTime: new Date(2020, 1),
150+
txData: {
151+
from: await polymathAPI.getAccount(),
152+
},
153+
});
154+
155+
// Add Template
156+
await VEW.addTemplate({
157+
name: TEMPLATE_NAME,
158+
numberOfTokens,
159+
duration: 100,
160+
frequency: 10,
161+
});
162+
163+
await tickerSecurityTokenInstance.approve({
164+
spender: VEWModuleAddress,
165+
value: new BigNumber(numberOfTokens),
166+
});
167+
168+
// Add Schedule
169+
await VEW.addScheduleFromTemplate({
170+
beneficiary,
171+
templateName: TEMPLATE_NAME,
172+
startTime: new Date(new Date().getTime() + 1 * 60000),
173+
});
174+
});

‎src/contract_wrappers/modules/wallet/__tests__‎

Whitespace-only changes.

‎src/contract_wrappers/modules/wallet/vesting_escrow_wallet_wrapper.ts‎

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
TransferResult,
3535
Perm,
3636
TransferStatusCode,
37+
FULL_DECIMALS,
3738
} from '../../../types';
3839
import {
3940
numberToBigNumber,
@@ -43,6 +44,7 @@ import {
4344
bytes32ToString,
4445
bytes32ArrayToStringArray,
4546
bigNumberToDate,
47+
stringToBytes32,
4648
} from '../../../utils/convert';
4749

4850
const TRANSFER_SUCCESS = '0x51';
@@ -442,7 +444,8 @@ export default class VestingEscrowWalletWrapper extends ModuleWrapper {
442444
};
443445

444446
public unassignedTokens = async () => {
445-
return (await this.contract).unassignedTokens.callAsync();
447+
const result = await (await this.contract).unassignedTokens.callAsync();
448+
return result.toNumber();
446449
};
447450

448451
public schedules = async (params: SchedulesParams) => {
@@ -484,13 +487,13 @@ export default class VestingEscrowWalletWrapper extends ModuleWrapper {
484487
const canTransferFromResult = await (await this.securityTokenContract()).canTransferFrom.callAsync(
485488
await this.getCallerAddress(params.txData),
486489
await this.address(),
487-
numberToBigNumber(params.numberOfTokens),
490+
valueToWei(numberToBigNumber(params.numberOfTokens), FULL_DECIMALS),
488491
'0x00',
489492
);
490493
assert.assert(canTransferFromResult[0] === TRANSFER_SUCCESS, 'Failed transferFrom');
491494

492495
return (await this.contract).depositTokens.sendTransactionAsync(
493-
numberToBigNumber(params.numberOfTokens),
496+
valueToWei(numberToBigNumber(params.numberOfTokens), FULL_DECIMALS),
494497
params.txData,
495498
params.safetyFactor,
496499
);
@@ -503,7 +506,7 @@ export default class VestingEscrowWalletWrapper extends ModuleWrapper {
503506
assert.assert(await this.isCallerAllowed(params.txData, Perm.Operator), 'Caller is not allowed');
504507
assert.assert(params.amount > 0, 'Amount cannot be zero');
505508

506-
const unassignedTokens = (await this.unassignedTokens()).toNumber();
509+
const unassignedTokens = await this.unassignedTokens();
507510
assert.assert(params.amount <= unassignedTokens, 'Amount is greater than unassigned tokens');
508511

509512
const canTransferResult = await (await this.securityTokenContract()).canTransfer.callAsync(
@@ -557,8 +560,8 @@ export default class VestingEscrowWalletWrapper extends ModuleWrapper {
557560
assert.assert(!(await this.getAllTemplateNames()).includes(params.name), 'Template name already exists');
558561
await this.validateTemplate(params.numberOfTokens, params.duration, params.frequency);
559562
return (await this.contract).addTemplate.sendTransactionAsync(
560-
params.name,
561-
numberToBigNumber(params.numberOfTokens),
563+
stringToBytes32(params.name),
564+
valueToWei(numberToBigNumber(params.numberOfTokens), FULL_DECIMALS),
562565
numberToBigNumber(params.duration),
563566
numberToBigNumber(params.frequency),
564567
params.txData,
@@ -603,11 +606,11 @@ export default class VestingEscrowWalletWrapper extends ModuleWrapper {
603606
assert.assert(params.templateName !== '', 'Invalid name');
604607
assert.assert(!(await this.getAllTemplateNames()).includes(params.templateName), 'Template name already exists');
605608
await this.validateTemplate(params.numberOfTokens, params.duration, params.frequency);
606-
await this.validateAddScheduleFromTemplate(params.beneficiary, params.templateName, params.startTime);
609+
await this.validateAddSchedule(params.beneficiary, params.templateName, params.startTime);
607610
return (await this.contract).addSchedule.sendTransactionAsync(
608611
params.beneficiary,
609-
params.templateName,
610-
numberToBigNumber(params.numberOfTokens),
612+
stringToBytes32(params.templateName),
613+
valueToWei(numberToBigNumber(params.numberOfTokens), FULL_DECIMALS),
611614
numberToBigNumber(params.duration),
612615
numberToBigNumber(params.frequency),
613616
dateToBigNumber(params.startTime),
@@ -624,7 +627,7 @@ export default class VestingEscrowWalletWrapper extends ModuleWrapper {
624627
await this.validateAddScheduleFromTemplate(params.beneficiary, params.templateName, params.startTime);
625628
return (await this.contract).addScheduleFromTemplate.sendTransactionAsync(
626629
params.beneficiary,
627-
params.templateName,
630+
stringToBytes32(params.templateName),
628631
dateToBigNumber(params.startTime),
629632
params.txData,
630633
params.safetyFactor,
@@ -641,7 +644,7 @@ export default class VestingEscrowWalletWrapper extends ModuleWrapper {
641644
// TODO: require(now < schedule.startTime, "Schedule started");
642645
return (await this.contract).modifySchedule.sendTransactionAsync(
643646
params.beneficiary,
644-
params.templateName,
647+
stringToBytes32(params.templateName),
645648
dateToBigNumber(params.startTime),
646649
params.txData,
647650
params.safetyFactor,
@@ -657,7 +660,7 @@ export default class VestingEscrowWalletWrapper extends ModuleWrapper {
657660
// TODO: _sendTokensPerSchedule assert
658661
return (await this.contract).revokeSchedule.sendTransactionAsync(
659662
params.beneficiary,
660-
params.templateName,
663+
stringToBytes32(params.templateName),
661664
params.txData,
662665
params.safetyFactor,
663666
);
@@ -683,6 +686,7 @@ export default class VestingEscrowWalletWrapper extends ModuleWrapper {
683686
public getSchedule = async (params: GetScheduleParams) => {
684687
this.checkSchedule(params.beneficiary, params.templateName);
685688
const result = await (await this.contract).getSchedule.callAsync(params.beneficiary, params.templateName);
689+
686690
return {
687691
numberOfTokens: result[0].toNumber(),
688692
duration: result[1].toNumber(),
@@ -710,7 +714,7 @@ export default class VestingEscrowWalletWrapper extends ModuleWrapper {
710714
public getScheduleCount = async (params: GetScheduleCountParams) => {
711715
assert.isNonZeroETHAddressHex('beneficiary', params.beneficiary);
712716
const result = await (await this.contract).getScheduleCount.callAsync(params.beneficiary);
713-
return result.toNumber();
717+
return weiToValue(result, FULL_DECIMALS).toNumber();
714718
};
715719

716720
/**
@@ -751,7 +755,7 @@ export default class VestingEscrowWalletWrapper extends ModuleWrapper {
751755
this.validateTemplate(params.numberOfTokens[i], params.durations[i], params.frequencies[i]),
752756
);
753757
resultValidateAddScheduleFromTemplate.push(
754-
this.validateAddScheduleFromTemplate(params.beneficiaries[i], params.templateNames[i], params.startTimes[i]),
758+
this.validateAddSchedule(params.beneficiaries[i], params.templateNames[i], params.startTimes[i]),
755759
);
756760
}
757761
const getAllTemplatesNames = await Promise.all(resultGetAllTemplatesNames);
@@ -763,9 +767,11 @@ export default class VestingEscrowWalletWrapper extends ModuleWrapper {
763767

764768
return (await this.contract).addScheduleMulti.sendTransactionAsync(
765769
params.beneficiaries,
766-
params.templateNames,
770+
params.templateNames.map(name => {
771+
return stringToBytes32(name);
772+
}),
767773
params.numberOfTokens.map(number => {
768-
return numberToBigNumber(number);
774+
return valueToWei(numberToBigNumber(number), FULL_DECIMALS);
769775
}),
770776
params.durations.map(duration => {
771777
return numberToBigNumber(duration);
@@ -795,7 +801,9 @@ export default class VestingEscrowWalletWrapper extends ModuleWrapper {
795801
await Promise.all(resultsValidateAddScheduleFromTemplate);
796802
return (await this.contract).addScheduleFromTemplateMulti.sendTransactionAsync(
797803
params.beneficiaries,
798-
params.templateNames,
804+
params.templateNames.map(name => {
805+
return stringToBytes32(name);
806+
}),
799807
params.startTimes.map(startTime => {
800808
return dateToBigNumber(startTime);
801809
}),
@@ -837,7 +845,9 @@ export default class VestingEscrowWalletWrapper extends ModuleWrapper {
837845

838846
return (await this.contract).modifyScheduleMulti.sendTransactionAsync(
839847
params.beneficiaries,
840-
params.templateNames,
848+
params.templateNames.map(name => {
849+
return stringToBytes32(name);
850+
}),
841851
params.startTimes.map(startTime => {
842852
return dateToBigNumber(startTime);
843853
}),
@@ -853,7 +863,13 @@ export default class VestingEscrowWalletWrapper extends ModuleWrapper {
853863
assert.assert(numberOfTokens % periodCount === 0, 'Invalid period count');
854864
const amountPerPeriod = numberOfTokens / periodCount;
855865
const granularity = await (await this.securityTokenContract()).granularity.callAsync();
856-
assert.assert(amountPerPeriod % granularity.toNumber() === 0, 'Invalid granularity');
866+
assert.assert(amountPerPeriod % weiToValue(granularity, FULL_DECIMALS).toNumber() === 0, 'Invalid granularity');
867+
};
868+
869+
private validateAddSchedule = async (beneficiary: string, templateName: string, startTime: Date) => {
870+
assert.isNonZeroETHAddressHex('beneficiary', beneficiary);
871+
assert.assert((await this.getScheduleCount({ beneficiary })) === 0, 'Already added');
872+
assert.assert(startTime.getTime() > Date.now(), 'Date in the past');
857873
};
858874

859875
private validateAddScheduleFromTemplate = async (beneficiary: string, templateName: string, startTime: Date) => {

0 commit comments

Comments
 (0)